객체지향 프로그래밍
객체지향 프로그래밍은 모든 데이터를 겍체로 취급하여 프로그래밍 하는 방법으로, 처리 요구를 받은 객체가 자기 자신의 안에 있는 내용을 가지고 처리하는 방식이다.

 클래스와 객체
클래스와 객체는 '붕어빵 틀'과 '붕어빵'의 관계다. 혹은 '제품의 설계도'와 '제품'의 관계라고 말할 수도 있다. 클래스는 구조체와 거의 같은 개념으로 함수까지 멤버로 넣어서 사용한다는 점이 다르다. 즉 변수와 함수들을 하나의 가방에 모아놓은 것이 클래스다. 우리가 어떤 클래스를 만들어서 정의하면, 그 클래스는 새로운 타입이 되는 것이다.

 // 간단한 클래스와 객체 예제
#include <iostream>
using namespace std;

// 클래스를 정의
class Point
{
public:
  int x, y;

  void Print();
};

// 멤버 함수 정의
void Point::Print()
{
  cout << "(" << x << ", " << y << ")\n";
}

int main()
{
  // 객체를 생성
  Point pt1, pt2;

  // pt1, pt2를 초기화 한다.
  pt1.x = 100;
  pt1.y = 100;
  pt2.x = 200;
  pt2.y = 200;

  // pt1, pt2의 내용을 출력한다.
  pt1.Print();
  pt2.Print();

  return 0;
}


 // 멤버 함수 안에서의 이름 충돌
#include <iostream>
using namespace std;

// 클래스 정의
class Point
{
public:
  int x, y;

  void Print();
};

// 멤버 함수 정의
void Point::Print()
{
  int x = 333;
  cout << "(" << x << ", " << y << ")\n";
}

int main()
{
  // 객체 생성
  Point pt1, pt2;

  // 객체 초기화
  pt1.x = 100;
  pt1.y = 100;
  pt2.x = 200;
  pt2.y = 200;

  pt1.Print();
  pt2.Print();

  return 0;
}

// 멤버 변수와 같은 이름의 지역변수들을 사용할 경우 나타나는 문제점을 보여주는 예제이다.

 생성자와 소멸자
생성자와 소멸자는 특별한 기능을 가진 멤버 함수다. 생성자는 객체를 생성할 때 자동적으로 호출되는 함수고, 소멸자는 객체가 소멸될 때 자동으로 호출되는 함수다.

 // 디폴트 생성자
#include <iostream>
using namespace std;

class Point
{
public:
  int x, y;

  // 멤버 함수
  void Print();

  // 생성자
  Point();  // 디폴트 생성자
};

// 멤버 함수 정의
void Point::Print()
{
  cout << "(" << x << ", " << y << ")\n";
}

// 디폴트 생성자 정의
Point::Point()
{
  x = 0;
  y = 0;
}

int main()
{
  Point pt;

  pt.Print();

  return 0;
}

// 디폴트 생성자에 의해서 x, y 멤버 변수가 0으로 초기화 된걸 알 수 있다.

 // 인자가 있는 생성자
#include <iostream>
using namespace std;

class Point
{
public:
  int x, y;

  // 멤버 함수
  void Print();

  // 생성자
  Point();  // 디폴트 생성자
  Point(int initialX, int initialY);  // 인자가 있는 생성자
};

// 멤버 함수 정의
void Point::Print()
{
  cout << "(" << x << ", " << y << ")\n";
}

// 디폴트 생성자 정의
Point::Point()
{
  x = 0;
  y = 0;
}

// 인자가 있는 생성자 정의
Point::Point(int initialX, int initialY)
{
  x = initialX;
  y = initialY;
}

int main()
{
  Point pt(35);

  pt.Print();

  return 0;
}


 // 복사 생성자
#include <iostream>
using namespace std;

class Point
{
public:
  int x, y;

  // 멤버 함수
  void Print();

  // 생성자
  Point();
  Point(int initialX, int initialY);

  // 복사 생성자
  Point(const Point& pt);
};

void Point::Print()
{
  cout << "(" << x << ", " << y << ")\n";
}

Point::Point()
{
  x = 0;
  y = 0;
}

Point::Point(int initialX, int initialY)
{
  x = initialX;
  y = initialY;
}

// 복사 생성자 정의
Point::Point(const Point& pt)
{
  x = pt.x;
  y = pt.y;
}

int main()
{
  Point pt1(100100), pt2(200200);  // 인자가 있는 생성자 호출

  Point pt3 = pt1;  // 복사 생성자 호출
  pt3.Print();

  pt3 = pt2;
  pt3.Print();

  return 0;
}


    FND 실습
1. 앞의 예제를 수정하여, 버튼을 누르면 2개의 FND가 동일한 숫자를 표시하도록 하기
#include "port.h"

int main(void)
{
  DDRF = 0xff;
  PORTF = 0xff;

  volatile unsigned char i = 0;
  volatile unsigned char j = 0;
  volatile unsigned char a = 0;

  while(1)
  {
    i = 0;

    a = ~PINC;

    while(a >= 1)
    {
      a /= 2;
      i++;
    }
    j = i;
    j <<= 4;
    PORTF = j | i;
  }
  return 1;
}
// '<<' 연산과 '|' 연산을 활용하여 구현

2. 0~99까지 순차적으로 숫자를 1씩 증가하면서 FND에 표시 하기
#include "delay_m.h"
#include "port.h"

int main(void)
{
  unsigned char fnd1 = 0x00;
  unsigned char fnd2 = 0x00;

  DDRF = 0xff;
  PORTF = 0x00;

  while(1)
  {
    PORTF = fnd2 | fnd1;
    fnd1++;
    if(fnd1 >= 0x0A)
    {
      fnd1 = 0x00;
      if(fnd2 == 0x90)
      {
        fnd2 = 0x00;
      }
      else
      {
        fnd2 += 0x10;
      }
    }
    delay(600);
  }
  return 0;
}

3. 0~99까지 순차적으로 숫자를 1씩 증가시키다가 버튼을 누르면 0부터 다시 시작하기.
#include "delay_m.h"
#include "port.h"

int main(void)
{
  unsigned char fnd1 = 0x00;
  unsigned char fnd2 = 0x00;

  DDRF = 0xff;
  PORTF = 0x00;
  DDRC = 0x00;
  PORTC = 0x00;

  while(1)
  {
    if((PINC & 0x01== 0)
    {
      fnd1 = 0x00;
      fnd2 = 0x00;
      PORTF = 0x00;
    }
    else
    {
      PORTF = fnd2 | fnd1;
      fnd1++;
      if(fnd1 >= 0x0A)
      {
        fnd1 = 0x00;
        if(fnd2 == 0x90)
        {
          fnd2 = 0x00;
        }
        else
        {
          fnd2 += 0x10;
        }
      }
      delay(600);
    }
  }
  return 0;
}

   C++ 예제
ex1)
  // cpp
#include <iostream>
using namespace std;

int main()
{
  int **pArray;
  int i, j;
  int cnt = 0;

  // int형 5*7 배열 동적 할당
  pArray = new int*[5];
  for(i = 0 ; i < 5 ; i++)
  {
    pArray[i] = new int[7];
  }
  
  // 각 배열에 차례되로 숫자 입력
  for(i = 0 ; i < 5 ; i++)
  {
    for(j = 0 ; j < 7 ; j++)
    {
      pArray[i][j] = cnt;
      ++cnt;
      cout.width(2);
      cout << pArray[i][j] << endl;
    }
  }
 
  // int형 5*7 배열 해제
  for(i = 0 ; i < 5 ; i++)
  {
    delete[] pArray[i];
  }
  delete[] pArray;


  return 0;
}
  // cpp
#include <iostream>
using namespace std;

int main()
{
  int (*p)[7];
  int i, j;
  int cnt = 0;

  // 5 * 7 int 형 배열 동적 할당
  p = (int (*)[7])new int[7 * 5];

  for(i = 0 ; i < 5 ; i++)
  {
    for(j = 0 ; j < 7 ; j++)
    {
      p[i][j] = cnt;
      ++cnt;
      cout << p[i][j] << endl;
    }
  }
  
  delete[] p;  // 동 적 할당 해제

  return 0;
}


  // c
#include <stdio.h>
#include <stdlib.h>

int main()
{
  int (*p)[7];
  int i, j;
  int cnt = 0;

  // 5 * 7 int형 배열 동적 할당
  p = (int (*)[7])malloc(5 * 7 * sizeof(int));

  for(i = 0 ; i < 5 ; i++)
  {
    for(j = 0 ; j < 7 ; j++)
    {
      p[i][j] = cnt;
      ++cnt;
      printf("p[%d][%d] = %d\n", i, j, p[i][j]);
    }
  }

  // 동적 할당 해제
  free(p);
  
  return 0;
}


ex2)
#include <iostream>
using namespace std;

int main()
{
  int *pArray;
  int num;

  cout << "동적 배열크기 입력: ";
  cin >> num;

  // 동 적 배열 할당
  pArray = new int[num];

  for(int i = 0 ; i < num ; i++)
  {
    pArray[i] = i;
    cout << "[" << i << "] = " << pArray[i] << endl;
  }

  // 동적 배열 해제
  delete[] pArray;

  return 0;
}


ex3)
#include <iostream>
using namespace std;

int main()
{
  int num;
  int min, sec;

  cout << "Input Seconds: ";
  cin >> num;

  min = num / 60;  // 분
  sec = num % 60;  // 초

  cout << min << " min " << sec << " sec" << endl;

  return 0;
}


ex4)
#include <iostream>
using namespace std;

int main()
{
  int num;
  int com;

  cout << "Input Number: ";
  cin >> num;

  com = (~num) + 1;  // 2의 보수

  cout << "2's complement(10진수): " << com << endl;
  cout << "2's complement(16진 수): " << hex << com << endl;

  return 0;
}



   LED 예제
  // port.h
#define PINA  (*(volatile unsigned int *)0x39)
#define DDRA  (*(volatile unsigned int *)0x3A)
#define PORTA  (*(volatile unsigned int *)0x3B)

#define PINB  (*(volatile unsigned int *)0x36)
#define DDRB  (*(volatile unsigned int *)0x37)
#define PORTB  (*(volatile unsigned int *)0x38)

#define PINC  (*(volatile unsigned int *)0x33)
#define DDRC  (*(volatile unsigned int *)0x34)
#define PORTC  (*(volatile unsigned int *)0x35)

#define PIND  (*(volatile unsigned int *)0x30)
#define DDRD  (*(volatile unsigned int *)0x31)
#define PORTD  (*(volatile unsigned int *)0x32)

#define PINE  (*(volatile unsigned int *)0x21)
#define DDRE  (*(volatile unsigned int *)0x22)
#define PORTE  (*(volatile unsigned int *)0x23)

#define PINF  (*(volatile unsigned int *)0x20)
#define DDRF  (*(volatile unsigned int *)0x61)
#define PORTF  (*(volatile unsigned int *)0x62)
  // delay_m.h
void delay(unsigned int ms);

void delay(unsigned int ms)
{
  unsigned int u = 0;
  unsigned int m = 0;

  while(1)
  {
    u++;
    if(u >= 1000)
    {
      m++;
      u=0;
    }
    if(m >= ms)
    {
      break;
    }
  }
}

ex1)
#include "delay_m.h"
#include "port.h"

int main(void)
{
  DDRF = 0xff;
  PORTF = 0xff;
  unsigned int flag = 1;

  while(1)
  {
    if(flag)
    {
      PORTF = 0x00;
      flag = 0;
    }
    else
    {
      PORTF = 0xff;
      flag = 1;
    }
    delay(500);
  }
}
// 주기적으로 LED가 켜졌다 꺼지는 코드

ex2)
#include "port.h"
#include "delay_m.h"

int main(void)
{
  DDRF = 0xff;
  PORTF = 0xff;
  unsigned char cnt = 0x00;

  while(1)
  {
    PORTF = ~cnt;
    delay(700);
    if(0xff == cnt)
    {
      cnt = 0x00;
      PORTF = 0xff;
    }
    cnt++;
  }
}
// 0부터 255까지 LED를 이용해서 카운트

ex3)
#include "port.h"
#include "delay_m.h"

int main(void)
{
  DDRF = 0xff;
  PORTF = 0xff;
  unsigned char num = 0x01;

  while(1)
  {
    PORTF = ~num;
    num <<= 1;
    if(num == 0x00)
    {
      num = 0x01;
    }
    delay(700);
  }
  return 0;
}
// 옆으로 한칸씩 LED ON

ex4)
#include "port.h"
#include "delay_m.h"

int main(void)
{
  DDRF = 0xff;
  PORTF = 0xff;
  unsigned char data = 0xff;
  unsigned int flag = 1;

  while(1)
  {
    PORTF = data;
    while((PINC & 0x01== 0x00)  // 1번 스위치가 눌러지면
    {
      if(flag)
      {
        if(0x00 == data)
        {
          data = 0xff;
        }
        else
        {
          data <<= 1;
        }
        flag = 0;
      }
    }
    flag = 1;
  }

  return 0;
}
// 1번 스위치를 누를 때마다 LED가 하나씩 켜진다. 8개 다 켜진 상태에서 다시 전부 OFF 된다.

struct map_form
{
        char sBsnsDate[8]; 
        char sCount[4];  
        struct ITEM item[1000]; /* ITEM의 크기는 88 */
};

위의 구조체 크기는 88012 일거라고 생각했다. 하지만 sizeof(struct map_form) 결과, 88016 이 나왔다.

메모리 할당 문제이다. 아래에 자세한 설명이 있다.

구조체가 메모리에 할당될 때는 일정한 규칙을 가진다. 일반적으로 우리가 생각하기에 어떤 구조체가 메모리에 할당될 때 
구조체의 첫 번째 멤버부터 순서대로 메모리에 차곡차곡  쌓여간다고 생각한다.

예를 들어 다음과 같은 구조체가 있다고 생각해보자.

struct StructA{
    char a;
    int b;
    char c;
    int d;
};

일반적으로 생각하기에 위의 구조체가 메모리에 할당되는 그림은 다음과 같다
[char a 1byte][int b 4byte][char c 1byte][int d 4byte]

그런데 문제는 이렇게 해서 전체 10바이트가 할당이 될 거라고 생각하면 오산 이라는 것이다. 
실제로 메모리에 할당되는 그림은 다음과 같다.
[char a 1byte][padding 3byte][int b 4byte][char c 1byte][padding 3byte][int d 4byte]

우선 눈에 들어오는 부분은 중간 중간에 padding데이터가 끼어있다는 것이다. 
좀더 자세히 살펴보면 메모리를 4바이트씩 묶을 수 있다는 사실을 발견할 수 있다. 그럼 반대로 4바이트씩 떼어서 보자.
[char a 1byte, padding 3byte] [int b 4byte] [char c 1byte, padding 3byte] [int d 4byte]

자 이렇게 보면 4바이트씩 데이터가 구분되어 있다는 것 이다. 이런 식으로 메모리를 사용하는 이유는 데이터를 읽고 쓸 때 보다 효율적으로 사용하기 위해서다. 
메모리에 할당할 때 4바이트씩 그 4바이트 안에 들어가지 않으면 나머지는 비워두고 다음 4바이트가 되는 주소에 넣어버리는 것이다. 때문에 위에서 보이는 것처럼 char a 다음에 int b가 바로 오지 않고 padding이 3바이트가 있는 것이다.

이렇게 구조체가 메모리에 할당될 때 일정한 규칙이 있는데,

1. 구조체의 멤버들 중에 가장 큰 데이터형을 찾는다. 
    -> int형이 가장 큰 데이터형 이므로 4byte
2. 그 데이터형의 크기와 맞게 메모리를 쪼갠다.
    -> 4byte씩 메모리를 나눈다. [4byte][4byte][4byte][4byte][4byte]
3. 멤버들을 차곡차곡 쌓아 넣는다.
    a. char a를 넣는다.
    [char a]
    b. int b를 넣어야 되는데 char a가 들어가 있는 곳에는 3바이트밖에 남아있지 않으므로 다음 메모리에 넣는다.
    [char a 1byte, padding 3byte][int b 4byte]
    c. char c를 넣는다.
    [char a 1byte, padding 3byte][int b 4byte][char c 1byte]
    d. int d를 넣어야 되는데 char c가 들어가 있는 곳에는 3바이트밖에 남아있지 않으므로 다음 메모리에 넣는다.
    [char a 1byte, padding 3byte][int b 4byte][char c 1byte, padding 3byte][int d 4byte]

이런 식으로 할당되는 것이다.

그런데 padding으로 채워진 부분 때문에 문제가 발생할 경우가 있으므로 그 padding을 없애는 방법을 알아보자.
결론부터 말하자면
#pragma pack(n) //n = 1, 2, 4, 8, 16 ...

구조체 선언부 위에 이 문장을 삽입하면 된다. 이것의 의미는 구조체를 정렬할 때 n바이트 단위로 정렬을 하라는 이야기다. 지정해준 n과 구조체의 멤버 중 가장 큰 멤버의 크기와 비교해서 더 작은 것에 맞추어서 정렬을 한다.

기본적으로 정렬을 할 때 멤버의 가장 큰 데이터형을 찾아서 메모리에 정렬을 하도록 되어있지만 위의 문장은 멤버 데이터형의 크기보다 작은 n바이트 정렬을 가능하게 한다. 그렇게 해서 padding을 없애는 것이다.

위의 예제를 아래와 같이 수정해보자

#pragma pack(2) //2바이트로 메모리를 정렬
struct StructA{
    char a;
    int b;
    char c;
    int d;
}

이렇게 한다면 메모리에 할당되는 그림은 아래와 같을 것이다.
[char a 1byte, padding 1byte][int b 2byte][int b 2byte][char c 1byte, padding 1byte][int d 2byte][int d 2byte]
padding이 2바이트로 줄었다. 

그리고 만약 #pragma pack(1) 로 설정한다면 padding은 완전히 사라지고 구조체의 크기는 10바이트로 정확히 맞추어질 것이다. 
#pragma pack(1)을 사용한 후 메모리 모습
[char a 1byte][int b 1byte][int b 1byte][int b 1byte][int b 1byte]
[char c 1byte][int d 1byte][int d 1byte][int d 1byte][int d 1byte]
   예제

1. 문자가 숫자인지 알려주는 isDigit 함수를 구현하세요. 문자 ch를 입력 받고, ch가 숫자인지 출력하세요. 맞다면 YES, 아니면 NO를 출력합니다.

#include <iostream>
using namespace std;

bool isDigit(char ch);  // 숫자 여부 판별 함수

int main()
{
  char ch;

  while(1)
  {
    cout << "문자: ";
    cin >> ch;

    if(isDigit(ch))  // 숫자이면
    {
      cout << "결과: YES" << endl;
    }
    else  // 숫자가 아니면
    {
      cout << "결과: NO" << endl;
    }
  }

  return 0;
}

bool isDigit(char ch)
{
  if(ch >= 48 && ch <= 57)  // '0' ~ '9'
  {
    return 1;
  }
  else
  {
    return 0;
  }
}


2. 정수 N을 입력하면 2 N승을 출력하는 프로그램을 작성하세요. , 비트 연산을 이용합니다.

그리고 -1을 입력할 경우 프로그램은 종료됩니다.

#include <iostream>
using namespace std;

int main()
{
  int num;
  unsigned int result = 0x01;

  while(1)
  {
    cout << "입력 : ";
    cin >> num;

    if(num == -1)  // -1을 입력하면
    {
      cout << "프로그램 종료" << endl;
      break;
    }

    result <<= num;  // num만큼 shift
    cout << "출력 : 2^" << num << = " << result << endl;
    result = 0x01;
  }

  return 0;
}


3. 다음과 같은 형태로 문자열이 출력되는 프로그램을 작성하시오.

1)
#include <iostream>
using namespace std;

int main()
{
  for(int i = 0 ; i < 5 ; i++)
  {
    for(int j = 0 ; j <= i ; j++)
    {
      cout << (char)('A' + j) << "\t";
    }
    cout << endl;
  }

  return 0;
}


2)
#include <iostream>
using namespace std;

int main()
{
  for(int i = 0 ; i < 5 ; i++)
  {
    for(int j = 0 ; j < 5 ; j++)
    {
      if(j == 4)
      {
        cout << 'a' << '\t';
      }
      else if(j == 3 && i >= 1)
      {
        cout << 'b' << '\t';
      }
      else if(j == 2 && i >= 2)
      {
        cout << 'c' << '\t';
      }
      else if(j == 1 && i >= 3)
      {
        cout << 'd' << '\t';
      }
      else if(j == 0 && i >= 4)
      {
        cout << 'e' << '\t';
      }
      else
      {
        cout << '\t';
      }
    }
    cout << endl;
  }

  return 0;
}



   인라인 함수

인라인 함수(Inline Function)는 새로운 종류의 함수가 아니고 함수의 속성이다. 인라인 함수의 기본 개념은 함수를 호출하는 대신에 함수의 내용을 그대로 옮겨 놓은 것이다. 인라인 함수를 사용하면 함수호출에 의해서 발생하는 오버헤드를 줄 일 수 있는 장점이 있다.
  // 인라인 함수 예제
#include <iostream>
using namespace std;

inline int SQUARE(int val)
{
  return val * val;
}

int main()
{
  cout << SQUARE(4<< endl;

  return 0;
}


   main함수의 인자
명령어에 인자가 있을 경우에 main() 함수는 인자를 가지게 된다. ANSI C에서 main() 함수의 원형은 다음과 같다.
int main(int argc, char *argv[])
첫 번째 인자 argc는 argument count에서 나온 말로 명령어 행에서 문자열의 개수이다. 문자열은 공백문자로 구분된다.
두 번째 인자 argv는 argument value에서 나온 말로 명령어 행의 각 문자열을 가리키는 포인터 배열이다.
   // main 함수 인자 예제
#include <stdio.h>

int main(int A, char *p[])
{
  int i;

  printf("%d\n", A);    // 명령어 인 자 개수
  
  for(i = 0 ; i < A ; i++)
  {
    printf("%d : %s\n", i, p[i]);  // 인 자들을 출력
  }

  return 0;
}


   구조체  
구조체는 의미상으로 연관된 여러 데이터를 하나의 이름으로 묶은 사용자 정의 자료형으로, 여러 개의 속성(구조체의 멤버)을 가지고 있는 개체를 정의할 수 있다. 다음은 기본적인 구조체 정의 형식이다.
struct 변수명
{
기본자료형 변수명;
기본자료형 변수명;
        ..........
};

구조체의 멤버는 도트연산자(.)를 사용하여 접근한다. 도트 연산자의 형식은 다음과 같다.
구조체변수.멤버이름
 // 구조체 예제
#include <stdio.h>

typedef struct  // typedef 을 이용하여 STUDENT 타입을 정의
{
  char name[20];
  int english;
  int math;
  float average;
} STUDENT;

int main()
{
  STUDENT st;  // 변수의 선언-메모리가 할당된다.

  // 키보드로부터 구조체 멤버의 값을 읽어들인다.
  printf("Please enter student name: ");
  gets(st.name);
  printf("Please enter english score: ");
  scanf("%d"&st.english);
  printf("Please enter math score: ");
  scanf("%d"&st.math);
  
  st.average = (float)(st.english + st.math) / 2;
  printf("student name: %s\n", st.name);
  printf("average: %6.3f\n", st.average);

  return 0;
}


 typedef
키워드 typedef은 새로운 자료형을 정의하는 데 사용된다. typedef은 전처리 문이 아닌 c문법이며, 긴 이름의 자료형을 짧게 정의하는 데 편리하게 이용된다. 또한 다른 시스템에 동일한 코드를 이식 할 때도 유용하게 사용 된다.
  // typedef 예제
#include <stdio.h>

typedef char NAME[20];  // 새로운 타입 정의
typedef int AGE;

int main()
{
  NAME student;  // 새로운 타입의 변수 선언
  AGE stAge;

  puts("Enter Name: ");
  gets(student);
  puts("Enter Age: ");
  scanf("%d"&stAge);

  printf("%s %d\n", student, stAge);
  
  return 0;
}


 구조체를 함수 인자로 전달
구조체를 함수 인자로 전달 할 때 call by value와 call by reference 둘 다 사용 할 수 있다. call by value 방법으로 전달하는 방법은 시스템에 부하가 많이 걸리므로 되도록이면 사용하지 않고 call by reference 방법을 이용하는 것이 좋다.


   함수 오버로딩
오버로딩이란 여러 함수들이 동일한 이름을 사용할 수 있는 기능을 말한다. 어떤 함수들이 이름은 똑같으면서 인자의 종류(시그니처)만 다르다면 이 함수들은 오버로드되었다고 말한다. 오버로딩을 사용함으로써 인자의 종류에 상관 없이 동일한 코드를 사용할 수 있다.
 기본적인 오버로딩 규칙
1. 컴퓨터는 인자의 타입을 확인해서 그에 가장 잘 어울리는 시그니처를 가진 함수를 호출하게 된다.
2. 반환 값만 틀린 경우는 오버로드 할 수 없다.
3. 시그니처가 다르더라도 오버로드 할 수 없는 경우
   void SameSignature(int i);
   void SameSignature(int& r);
 적당한 함수를 찾는 순서
1. 정확하게 일치하는 경우(Exact Match)
2. 승진에 의한 형변환(Promotion)
3. 표준 형변환(Standard Conversions)
4. 사용자에 의한 형변환(User-defined Conversions

   // 함수 오버로딩을 보여주는 예제
#include <iostream>
using namespace std;

void function(void)
{
  cout << "Function(void) call" << endl;
}

int function(char c)
{
  cout << "Function(char c) call" << endl;
}

void function(int a, int b)
{
  cout << "Function(int a, int b) call" << endl;
}

int main()
{
  function();  // void function(void) 함수 호출
  function('a');  // int fuction(char c) 함수 호출
  function(1213);  // void function(int a, int b) 함수 호출

  return 0;
}
 


 디폴트 인자
디폴트 인자는 우리가 따로 값을 지정해주지 않은 경우에 선택하는 인자의 값을 말한다. 디폴트 인자의 경우에는 함수를 호출 할 때 해당 인자의 값을 적어주지 않아도 된다.
  // 디폴트 인자를 보여주는 예제
#include <iostream>
using namespace std;

int boxvolume(int length, int width = 1int height = 1);

int main()
{
  cout << "[3, 3, 3]  : " << boxvolume(333<< endl;
  cout << "[5, 5, def]  : " << boxvolume(55<< endl;
  cout << "[7, def, def]  : " << boxvolume(7<< endl;

  return 0;
}

int boxvolume(int length, int width, int height)
{
  return length * width * height;
}


   동적할당
동적 메모리 할당은 말 그대로 메모리를 동적으로 할당한다는 뜻이다. 메모리의 크기를 프로그램이 실행되는 도중에 동적으로 결정하여 할당하며, 메모리를 할당하고 해제하는 시점이 자유롭다. 단, 동적 메모리 할당으로 할당한 메모리는 사용자가 직접 해제해주기 전까지는 절대로 컴퓨터에 위해서 해제되는 일이 없으므로 신경써서 해제해주지 않으면 메모리 누수가 일어난다.

 연산자 new, delete, new[], delete[]

new, delete는 변수 하나를 동적으로 할당하는 것이고 new[], delete[]는 배열을 동적으로 할당할 때 사용한다.
  // 동적 할당 예제
#include <iostream>
using namespace std;

int main()
{
  int size;

  cout << "할당하고자 하는 배열의 크기";
  cin >> size;

  int *arr = new int[size];

  for(int i = 0 ; i < size ; i++)
  {
    arr[i] = i + 10;
  }
  for(int j = 0 ; j < size ; j++)
  {
    cout << "arr[" << j << "] = " << arr[j] << endl;
  }
  delete[] arr;

  return 0;
}


 동적 메모리 할당과 관련된 기본적인 규칙
1. new, delete와 new[], delete[] 쌍을 맞춰서 사용하자.
2. NULL 포인터를 해제하는 것은 안전하다.
3. 해제한 메모리를 또 해제해서는 안 된다.
  // 할당 받은 메모리를 해제할 때..
#include <iostream>
using namespace std;

int main()
{
  // 메모리를 할당한다.
  short *p = new short[100];

  cout << "p = " << p << endl;
  delete[] p;
  p = NULL;

  cout << "p = " << p << endl;
  delete[] p;
  p = NULL;

  return 0;
}
// 할당 받은 메모리를 해제 할 때, 그 공간은 더 이상 유효하지 않으므로 그 공간을 가리키는 포인터 변수를 NULL로 초기화 함으로써 더 신뢰성 있는 코드를 작성할 수 있다.

 동적 할당 내부 동작

기본적으로 c프로그램의 메모리 구조는 다음과 같다. 동적할당을 하게 되면 Heap영역에 메모리가 할당된다.
Stack : 지역변수
Heap : 동적할당
bss : 초기화 되지 않은 전역변수 or static변수
Data : 초기화 된 전역변수 or static변수
일반적으로 각 영역에는 다음과 같은 변수들이 할당된다.

   문자열
문자열은 문자형 배열이다. 문자형 상수는 작은따옴표를 이용하여 표시하고, 문자열 상수는 큰 따옴표로 표시한다. 문자열은 컴파일러에 의해 자동적으로 맨 마지막에 NULL 문자('\0')가 들어간다.
  // 문자열 길이를 알아보는 프로그램
#include <stdio.h>
#include <string.h>

int main()
{
  int length;
  int size;

  length = strlen("string");
  size = sizeof("string");

  printf("string length : %d\n", length);
  printf("string size : %d\n", size);
  
  return 0;
}

// strlen()함수는 정확히 문자열 길이를 출력해준다. sizeof는 변수의 메모리 공간 크기를 나타낸다.

  // strlen()함수를 직접 만들어봄
#include <stdio.h>

int strlen2(const char *p);

int main()
{
  int iNum;

  iNum = strlen2("test");
  printf("iNum = %d\n", iNum);

  return 0;
}

int strlen2(const char *p)
{
  int iCnt;

  for(iCnt = 0 ; *p != 0 ; iCnt++)
  {
    ++p;  // 포인터 위치를 한 칸씩 뒤로 옮김
  }

  return iCnt;
}


  // 문자열 함수를 이용하는 프로그램
#include <stdio.h>
#include <string.h>

int main()
{
  char stringA[80= "everywhere people stare ";
  char stringB[80= "every and each day";
  char stringC[80= "";

  char ch = '\0';
  char *pch = &ch;

  printf("stringA : %s\n", stringA);
  printf("length of stringA : %d\n", strlen(stringA));

  // stringA 와 stringB의 비교
  printf("compare string: %d\n", strcmp(stringA, stringB));
  printf("compare string: %d\n", strncmp(stringA, stringB, 5));
  
  // stringC 에 stringA 복사
  strcpy(stringC, stringA);
  printf("stringC : %s\n", stringC);
  printf("stringC : %s\n\n", strncpy(stringC, stringB, 10));

  // stringA에 stingB 붙이기
  strcat(stringA, stringB);
  printf("%s\n\n", stringA);

  // 문자열에서 'y'문자 검색
  printf("stringB : %s\n", stringB);
  pch = strchr(stringB, 'y');
  if(pch == NULL)
  {
    printf("Not exist\n");
  }
  else
  {
    printf("'y' position %d from head\n", pch - stringB);
  }

  pch = strrchr(stringB, 'y');
  if(pch == NULL)
  {
    printf("Not exist\n");
  }
  else
  {
    printf("'y' position %d from tail\n", pch - stringB);
  }
  return 0;
}

프로그래밍을 공부하기 위해서는 언어도 필수 이지만 시스템에 대한 이해도 필수 입니다.(...라고 본인은 생각합니다;;) 시스템에서도 핵심은 메모리 인데요...메모리 구조를 제대로 알아야 최적화를 비롯한 자료구조 등의 프로그래밍을 쉽게 공부 할 수 있습니다.

 

 위의 그림은 C에서의 메모리 레이아웃을 나타낸 그림입니다. 아마 프로그래밍을 공부하신 분이라면 한번 쯔~음은 보셨을 것입니다.

 우선 ROM부터 설명하겠습니다. ROM은 일반적인 데스크탑이라면 하드디스크(HDD)일 것이고, 임베디드 시스템이라면 보통 Flash ROM을 말합니다... 컴파일로 생성된 실행 파일이나...시스템이 동작하기 위한 이미지 파일이 ROM에 저장되어 있을 것입니다.

 ROM의 Text 섹션은 보통 프로그램 코드 (함수나 일반 처리문 같은....)와 읽기 전용 데이터(RO_data, 상수 데이터)로 이루어져있습니다. 

 Data 섹션은 읽기 쓰기가 가능한 (RW_data) 데이터가 들어가는데 그 예로 전역 변수나 정적 변수를 들 수 있습니다.

 ROM - Text : Code + RO_data

           Data : RW_data


 RAM은 최근 대부분의 구성이 보호 모드로 가상 메모리를 사용하게 되어있습니다. 물론 실제 모드로 위의 그림과 같이 세그먼트를 분리하여 사용하기도 하지만 그래도 가상 메모리 구조와 별차이 없게 사용하고 있습니다.

 시스템을 구동 할 경우 어셈블리로 스타트업 코드를 구현하다가 C언어의 main()으로 제어를 넘기는데 그 이유가 위와 같이 레이아웃이 설정되어 있어야 하기 때문입니다. 어셈블리로 구현된 스타트업 코드에서 가상메모리 페이지를 관리하는 MMU(Memory Management Unit)를 구동시키거나 세그먼트를 분리해야 C 코드로 제어를 넘길 수 있습니다.

 가상 메모리는 말그대로 보호 모드(말 그대로 각 프로세스. 즉, 메모리에 올라와 실행되는 프로그램마다 각자 고유의 메모리 영역을 침범되지 않게 보호되어야 하는...)로 프로세스마다 32비트 시스템을 경우 4GB(2의 32승)의 가상메모리 영역을 가지게 되고, 각 프로서스마다 공유되는 커널 영역과 프로세스마다 고유한 유저 영역 메모리의 구조를 가지게 됩니다. 실제로 프로그램의 메모리 영역을 안다는 것이 위의 구조 중 유저영역을 이해한다는 것과 같다고 보면 될것입니다. 보통 윈도우즈의 경우 하위(0x0000 0000 ~ 0x7FFF FFFF) 2GB가 유저 영역, 상위 2GB(0x8000 0000 ~ 0xFFFF FFFF)의 커널 영역을 가지고 , 리눅스의 경우 상위 1GB 커널, 하위 3GB 유저 영역을 가집니다. (지역 변수의 경우 위의 그림의 경우 스택에 저장되기 때문에 & 연산자를 이용해 주소 값을 보면, 윈도우즈일 경우 주소가 0x7FFF FFFFF 보다 작으면서 가까운 주소가 출력 될 것입니다.)

 RAM의 Text영역은 ROM의 Text 영역을 그대로 가지고 옵니다. 하지만 임베디드 시스템의 경우 고정된 값이기 때문에 RAM으로 옮길 필요는 없습니다. 그래서 ROM에서 그대로 활용할 경우도 있으며 RAM공간에 여유가 있고 빠른 속도(RAM이 CPU에 가까우므로 빠르죠...)를 필요로 할 경우 RAM에 복사를 해서 사용 합니다.

 Data 영역은 그대로 ROM의 Data 영역을 복사해서 사용합니다.

 bss 영역은 ZI_data(Zero Initialoze). 즉, 초기화 되지 않은 전역 변수나 정적 변수로 구성된 세그먼트 입니다. 아마도 실행 파일과 같은 이미지 파일에서 메타 데이터 정보만 가지고 있다가 실제 RAM에 구성될 때 공간을 확보하는 것 같습니다. 실제 데이터가 없는데 ROM에 공간을 사용하는 것은 낭비니까 당연하겠네요...

 Heap은 동적 메모리가 할당되는 영역입니다. (완전 이진 트리를 나타내는 자료 구조인 Heap과 헛갈리시면 않됨미...) C의 mallloc()이나 C++의 new 할당자로 공간을 확보합니다. 사용 완료 후 해제를 해야하겠지요...이 영역은 단편화(Fragmentation) 문제와 관련된 영역이기도 합니다.

 Stack은 지역 변수를 기억하는 공간으로 LIFO 구조를 가집니다. 이 영역이 다른 영역에 비해서 복잡하면서도 구조를 제대로 이해하면 조금 재미있는 일(?)을 할 수 있습니다. 이 구조를 이해하기 위해서 스택 프레임(Stack Frame)을 한번 쯤 공부해보면 좋습니다.

RAM - Text : Code, RO_data

        Data : RW_data

        bss   : ZI_data

        Heap : 동적 할당 data

        Stack : LIFO구조로 저장되는 지역 변수 data

 

 물론 위의 가상 메모리 구조는 기본틀이고, 심볼 테이블(Symbol Table)과 같은 추가적인 정보를 포함하고 있습니다. 컴파일러의 구조를 한번 공부해보면 도움이 될 것 같네요...


const와 포인터

const라는 키워드는 포인터에 두 가지 방법으로 사용된다. 첫 번째 방법은 상수 객체를 지시하는 포인터를 만드는 것이다. 상수 객체를 지시하는 포인터를 사용하여 그 포인터가 지시하는 값을 변경할 수 없다. 두 번째 벙법은 자신을 상수로 만드는 것이다. 상수 포인터를 사용하여 그 포인터가 지시하는 장소를 변경할 수 없다.
int age = 39;
int sage = 25
const int *pt = &age;    // 첫 번째 방법
int * const pt1 = &sage;    // 두 번째 방법
*pt = 40;        // 잘못된 사용
pt = &sage;        // 사용 가능
*pt1 = 40;        // 사용가능
pt1 = &age;        // 잘못된 사용

다차원 배열

c언어에서는 1차원 배열을 확장시켜 일반화한 다차원 배열을 지원한다. 2차원 배열은 1차원 배열의 각 원소가 배열로 구성된 배열의 배열이다.
code
#include <stdio.h>

int main()
{
  int i, j;
  int array[3][4= {
    {15234556},
    {34527623},
    {43629184},
  };

  for(i = 0 ; i < 3 ; i++)
  {
    for(j = 0 ; j < 4 ; j++)
    {
      printf("%d ", array[i][j]);
    }
    printf("\n");
  }

  return 0;
}


// 다차원 배열을 보여주는 예제

code
#include <stdio.h>

int main()
{
  int a[5= {};
  int b[3][4= {};
  
  printf("&a[4] = %p\n"&a[4]);
  printf("(a + 4) = %p\n\n", (a + 4));

  printf("b = %p\n", b);
  printf("&b[0][0] = %p\n"&b[0][0]);
  printf("&b[1][0] = %p\n"&b[1][0]);
  printf("&b[2][0] = %p\n\n"&b[2][0]);

  printf("b[0] = %p\n", b[0]);
  printf("b[1] = %p\n", b[1]);
  printf("b[2] = %p\n\n", b[2]);

  printf("(b + 0) = %p\n", (b + 0));
  printf("(b + 1) = %p\n", (b + 1));
  printf("(b + 2) = %p\n\n", (b + 2));

  printf("&a + 1 = %p\n"&a + 1);
  return 0;
}


// 다차원 배열 구성 요소들의 의미를 잘 보여주는 예제

* c언어에서는 배열 자체가 인수로 전달될 수 없다. 즉 call by value방법으로 인수 전달은 가능하지 않다. 배열을 함수 인자로 넘길 때는 call by reference를 사용한다.

c++ 기본

c++언어를 이용하여 c언어를 처음 접할 때, 작성하는 "Hello, World" 프로그램에 입력 받는 코드까지 간단히 작성해 보았다
code
#include <iostream>

int main()
{
  int a;
  int b;

  std::cout << "Hello, World\n";
  std::cin >> a >> b;
  std::cout << a << "\t" << b << std::endl;

  return 0;
}


// c++에서는 iostream을 기본적으로 include 시킨다.
// 출력은 cout << 이용하고, 입력은 cin >> 이용한다.

namespace

프로그램밍 프로젝트 규모가 커짐에 따라, 변수명이라든지 함수명 등 이름 충돌의 가능성이 높아지고 있다. 이를 위해 c++표준은 이름 사용 범위를 더 잘 제어할 수 있도록 namespace이라는 기능을 제공한다.
code
#include <iostream>

namespace A    // namespace A
{
  void printTest()
  {
    std::cout << "namespace A test\n";
  }
}

namespace B    // namespace B
{
  void printTest()
  {
    std::cout << "namespace B test\n";
  }
}

using namespace B;  // B:: 사용하지 않고 함수 등을 사용가능하게 해준다

int main()
{
  A::printTest();  // A namespace에 있는 printTest()함수를 실행
  printTest();  // B namespace에 있는 printTest()함수를 실행

  return 0;
}


// namespace의 사용의 예를 보여주는 코드

bool

c++에서 지원하는 논리 자료형
code
#include
 <iostream>
using namespace std;

int main()
{
  bool b1;
  bool b2;

  b1 = true;
  b2 = false;

  cout << "b1 = " << b1 << endl;
  cout << "b2 = " << b2 << endl;

  return 0;
}


// bool 자료형은 true(1), false(0)을 저장 할 수 있다.

Reference

c++는 Reference라는 새로운 복합형 언어를 추가했다. Reference는 미리 정의된 어떤 변수의 실제 이름 대신 쓸 수 있는 대용 이름이다. Reference의 주된 용도는 함수의 인자에 사용하는 것이다. Reference를 전달인자로 사용하면, 그 함수는 복사본 대신 원본 데이터를 가지고 작업한다.
code
#include <iostream>
using namespace std;

int main()
{
  int target = 20;

  int& ref = target;    // Reference 생 성

  cout << "ref = " << ref << endl;
  cout << "target = " << target << endl;
  cout << "&ref = " << &ref << endl;
  cout << "&target = " << &target << endl;

  ref = 100;

  cout << "ref = " << ref << endl;
  cout << "target = " << target << endl;

  return 0;
}


code
#include
 <iostream>
using namespace std;

void swap(int&int&);

int main()
{
  int x = 10;
  int y = 20;
//  int& refx = x;
//  int& refy = y;
  
  cout << "x = " << x << " y = " << y << endl;
//  swap(refx, refy);
  swap(x, y);
  cout << "x = " << x << " y = " << y << endl;

  return 0;
}

void swap(int& a, int& b)  // Reference 를 이용한 함수 인자 전달
{
  int tmp;

  tmp = a;
  a = b;
  b = tmp;

  return;
}


// Reference를 이용하면 포인터를 사용하지 않고도 main 함수에 있는 두 지역 변수를 서로 swap 시킬 수 있음을 알 수 있다.
// 현재 주석 처리 된 부분으로 swap 함수를 호출해도 정상 호출 된다.

다중 포인터

int i = 100;
int *p = &i;
int **pp = &p;
int ***ppp = &pp;

변수

주소

i

100

1000

p

1000

996

pp

996

992

ppp

992

988

위 선언을 대충 그림으로 나타내면 다음과 같다. (주소는 임의로 정한 값이다)


code
#include <stdio.h>

int main()
{
  int i = 100;
  int *p = &i;
  int **pp = &p;
  int ***ppp = &pp;

  printf("i = %d\n", i);
  *p = 90;
  printf("i = %d\n", i);
  **pp = 80;
  printf("i = %d\n", i);
  ***ppp = 70;
  printf("i = %d\n", i);
  
  return 0;
}

// *p, **pp, ***PPP 모두 i임을 알 수 있다

배열

배열은 사용자가 의미상의 연관이 있는 동일한 자료형에 속한 여러 개의 자료들을 묶어 하나의 이름으로 정의한 자료형이다. 예를 들어 25명으로 구성된 한 학급의 시험 성적을 관리한다고 할 때, 50개의 독립된 변수로 나타내었을 시 코드는 굉장히 길어 질 것이다. 이런 경우 배열을 사용하면 프로그래밍을 간단히 하면서도 변수들 사이의 연관성을 컴퓨터 내부적으로도 잘 나타낼 수 있다.
    자료형 배열명[크기];        // 배열의 선언 형식
code
#include <stdio.h>

int main()
{
  int score[10];
  int i;
  int sum = 0;
  float average;

  for(i = 0 ; i < 10 ; i++)
  {
    printf("Please enter score[%d] : ", i);
    scanf("%d"&score[i]);
  }
  for(i = 0 ; i < 10 ; i++)
  {
    sum += score[i];
  }
  average = (float)sum / 10;
  printf("average score : %f\n", average);
  
  return 0;
}


// 점수 10개를 입력 받아 평균을 구하는 프로그램

배열과 포인터는 컴퓨터 내부적으로 거의 같은 방법으로 메모리에 접근한다. 그러나 이 둘 사이의 차이를 잘 구분해야 한다. 포인터는 메모리의 주소를 가지는 변수로 그 주소를 이용해서 메모리에 접근하는 것에 비해, 배열명은 그 배열에 할당된 메모리의 시작주소를 나타내는 상수이다.
code
#include <stdio.h>

int main()
{
  int BSP[25= {123, };
  int *p = BSP;

  printf("BSP size : %d \n"sizeof(BSP));
  printf("BSP : %p \n", BSP);
  printf("BSP[0] : %p \n"&BSP[0]);
  printf("BSP[1] : %p \n"&BSP[1]);
  printf("BSP+1 : %p \n", BSP + 1);
  printf("&BSP+1 : %p \n"&BSP + 1);
  printf("BSP : %p \n"&BSP);
  printf("\n");
  printf("*p = %d\n", *p);
  printf("*(p + 1) = %d\n", *(p + 1));
  printf("*(p + 2) = %d\n", *(p + 2));
  printf("\n");
  printf("p[0] = %d\n", p[0]);
  printf("p[1] = %d\n", p[1]);
  printf("p[2] = %d\n", p[2]);
  printf("\n");
  BSP[1= 10;
  printf("*(BSP + 1) : %d \n", *(BSP + 1));

  return 0;
}


// 위 코드로 배열과 포인터의 관계를 잘 알 수 있다.
// "&배열명"에 +1 연산을 하면 전체 배열 크기만큼 증가한다.
// 배열은 선언 시에만 { } 안에 값을 넣어서 초기화 할 수 있다.


ucOS-II Porting Os(운영체제)

2007/06/07 17:20

복사 http://blog.naver.com/induction04/90018431532

uCOS-II 포팅

1. 포팅(Porting)이란

- 리얼 타임 커널을 마이크로프로세서나 마이크로컨틀롤러에서 사용할수 있도록 하는 작업을 말한다.

2. uCOS-II를 포팅하기 위해 필요한 일반적인 조건(5가지)

- 재진입(Reentrancy)을 지원하는 코드를 생성할 수 있는 C컴파일러(선점형 커널이므로)

- C언어에서 인터럽트 비활성화, 활성화를 지원

- 인터럽트 지원및 일정한 주기로 발생하는 타이밍 인터럽트를 제공해야 한다.(주기는 초당 10회에서 100회정도)

- 프로세서 수준에서 지원하는 적정크기의 하드웨어 스택기능(수 KByte정도 크기)

- 스택포인터와 레지스터를 스택이나 메모리로 저장하고 가져올 수 있는 프로세서 명령어

 

3. uCOS-II 하드웨어/소프트웨어 구조

하드웨어(CPU, 타이머) + 소프트웨어 (uCOS-II포트, -> uCOS-II(프로세서 독립코드), uCOS-II설정(응용프로그램 의존코드), -> 응용 소프트웨어)

- uCOS-II포트 (포팅을 완료한 프로세서 의존적인 코드) : OS_CPU.H, OS_CPU_A.H, OS_CPU_C.H

- uCOS-II(프로세서 독립코드) : OS_CORE.C, OS_MBOX.C, OS_MEM.C, OS_Q.C, OS_SEM.C, OS_TASK.C, OS_TIME.C,  (uCOS_II.C, uCOS_II.H,)  

- uCOS-II설정 : OS_CFG.H, INCLUDES.H

4.포팅 작업순서

- #define 상수값을 설정(OS_CPU.H)

- 10개의 데이터 타입을 정의한다.(OS_CPU.H)

- 3개의 매크로 함수를 정의한다. (OS_CPU.H)

- 6개의 간단한 C언어 함수를 작성(OS_CPU_C.H)

- 4개의 간단한 어셈블리 함수를 작성한다.(OS_CPU_A.ASM)

 

<<<ucos-II 공식 웹사이트>>>

http://www.ucos-ii.com/

FND data 전송

main.c
#include "port.h"
#include "delay_m.h"

int main(void)
{
  unsigned char fnd1 = 0x00;
  unsigned char fnd2 = 0x00;
  int sw_flag = 1;      // FND OUTPUT flag
  int click_flag = 0;
  int click_flag1 = 1;

  DDRF = 0xff;  // FND OUTPUT PIN
  PORTF = 0x00;
  DDRE = 0x00;  // SWITCH INPUT PIN
  PORTE = 0x00;
  DDRC = 0x01;  // CONTROL PIN
  PORTC = 0x00;
  DDRD = 0x00;  // DATA TRANSFER
  PORTD = 0x00;

  while(1)
  {
    // 
클릭 감지 부분
    if((PINE & 0x01== 0)  //  SWITCH PUSH
    {
      if(click_flag1)  // 
처음 실행..스위치가 떨어질 때까지 실행 되지 않음
      {
        click_flag = 1;
        click_flag1 = 0;
        sw_flag = !sw_flag; // toggle
        if(!(sw_flag))  // 
현재  전송
        {
          PORTC = 0x01;
          DDRD = 0xff;
          PORTD = fnd2 | (fnd1 - 1);
          delay(100);
          PORTC = 0x00;
        }
      }
    }
    else if((PINE & 0x01&& click_flag) // 
스위치가 떨어 졌을 
    {
      click_flag1 = 1;
    }
    
    // 
수신부
    if(PINC & 0x02)
    {
      DDRD = 0x00;
      fnd1 = 0x0& PIND;
      fnd2 = 0xf0 & PIND;
      PORTF = fnd2 | fnd1;
    }
    
    // FND 
카운트 부분
    if(sw_flag)
    {
      PORTF = fnd2 | fnd1;
      fnd1++;

      if(fnd1 >= 0x0A)
      {
        fnd1 = 0x00;
        if(fnd2 >= 0x90)
        {
          fnd2 = 0x00;
        }
        else
        {
          fnd2 += 0x10;
        }
      }
      delay(500);
    }
  }
  return 0;
}
// 2대의 DK128를 연결해서 FND 카운트 값을 서로 전송하는 코드이다. A DK128에서 먼저 FND에 99까지 카운트 시작, 카운트 도중에 버튼을 입력하면 카운트가 멈추고 현재 카운트 된 값이 B DK128로 전송된다. B DK128은 이 값을 받아 자신의 FND에 출력한다.
// 양 쪽에 같은 값이 찍힌 상태에서 스위치를 누르면 다시 카운트가 시작한다.

Call by reference

함수 인자 값으로 포인터를 사용함으로써 다른 함수에서 현재 함수의 변수들의 값들을 바꿀 수 있다.
p6-6.c
#include <stdio.h>

void swap(int *, int *);

int main()
{
  int x = 7;
  int y = 9;

  printf("A: initial value x = %d,  y = %d\n", x, y);
  printf("B: &= %p,  &= %p\n\n"&x, &y);
  swap(&x, &y);
  printf("G: end of main x = %d, y = %d\n", x, y);
  
  return 0;
}

void swap(int *px, int *py)
{
  int temp;
  printf("C: px = %p,  py = %p\n", px, py);
  printf("D: *px = %d,  *py = %d\n\n", *px, *py);

  temp = *px;    // main
함수 x변수와 y변수 값을 바꾼다.
  *px = *py;
  *py = temp;

  printf("E: px = %p,  py = %p\n", px, py);
  printf("F: *px = %d,  *py = %d\n\n", *px, *py);

}


// Call by Reference를 보여주는 예제이다. main함수의 x변수와 y변수 값을 swap함수에서 서로 바꾸고 있다. 즉 main함수의 지역변수를 swap함수에서 서로 바꾸기 위해 각 지역변수의 포인터를 인자로 넘겨주고 있다.

재귀함수

함수 자신이 스스로 자신을 호출하는 함수를 재귀함수라고 한다. 재귀함수는 코드 가독성이 떨어 질뿐만 아니라 시스템 자원 활용에 있어서도 매우 비효율적이므로 되도록이면 사용하지 않는 것이 좋다.
p6-8.c
#include <stdio.h>

void recursive_print(int);

int main()
{
  int num;

  printf("Please enter a positive integer: ");
  scanf("%d"&num);
  recursive_print(num);

  return 0;
}

void recursive_print(int n)
{
  if(n <= 0)
  {
    return;
  }
  else
  {
    printf("recursive_print: n = %d\n", n);
    recursive_print(n - 1);  // 스스로 자신을 호출
  }
}


// 숫자를 입력 받아 1씩 감소 시키면서 그때 그때 숫자들을 화면에 출력하는 프로그램이다.

함수 포인터

함수의 이름은 함수의 시작지점을 가리키는 포인터이다. 즉 함수의 시작지점을 저장할 수 있는 포인터를 함수 포인터라 한다. 함수 포인터는 리턴타입과 인자들에 대한 정보를 가지고 있어야만 한다.
에를 들어 int printf(const char *format, ...)는 printf함수의 원형이다. 이 함수의 함수 포인터 타입은 int (*)(const char *, ...)이 된다
main.c
#include <stdio.h>

void test(int A);

int main()
{
  void (*fp)(int);    // 함수포인터 선언
  fp = test;        // 함수포인터에 test대입
  fp(10);

  // test, printf, main, scanf 함수의 시작 주소를 출력
  printf("test : %p\n", test);
  printf("printf : %p\n", printf);
  printf("main : %p\n", main);
  printf("scanf : %p\n", scanf);

  ((void(*)(int))0x8048432)(100);        // test함수를 시작 주소로 출력
  ((int(*)(const char *, ...))0x80482c4)("test...\n");    // printf함수를 시작 주소로 출력

  return 0;
}

void test(int A)
{
  printf("%d\n", A);
  return;
}


// 함수 포인터를 사용한 예이다. 함수의 시작주소만 알면 그 주소를 각 함수의 포인터 타입에 맞게 캐스팅 연산 후 호출 할 수 있다.

+ Recent posts