OFFICE/프로그램

typedef && 전처리기 에 대해서

gandus 2012. 11. 29. 11:36

2, typedef

 typedef 는 형재정의라 합니다.

typedef    int    INT;

이 문장은 int 자료형을 INT라고도 하겠다는 것입니다.

 

예제를 보면 쉽게 이해할 수 있습니다.

typedef int INT;
void main ( )
{
    INT n = 10;
    printf("%d\n", n);
}
  1. 10

int 대신에 INT라는 이름을 사용하여 int형 변수 n을 만들고 출력합니다.

 

포인터형을 재정의할 수 있습니다.

typedef int *PINT;
void main ( )
{
    int n = 10;
    PINT pn = &n;

    printf("%d %d\n", n, *pn);
}
  1. 10 10

 PINT는 int*형을 의미합니다.

 

아래처럼 여러개를 정의할 수도 있습니다.

typedef int INT, *PINT;
void main ( )
{
    INT n = 10;
    PINT pn = &n;

    printf("%d %d\n", n, *pn);
}
  1. 10 10

 INT는 int 자료형을 PINT는 int* 자료형을 의미합니다.

 

이쯤되면 굳이 typedef를 사용해야 할까?라는 의문이 생깁니다. 당연히 간단한 코드에는 typedef를 사용하지 않고 복잡한 자료형에 typedef를 사용합니다.

특히, 구조체나, 함수포인터, 배열포인터와 같이 복잡한 자료형에 유용하게 사용됩니다. 지금은 간단한 사용법만 잘 알아 두세요. 나머지는 모두 다른 예제들을 공부할 때 배우게 됩니다.

구조체를 typedef한 예제입니다. 13. 구조체의 예제입니다.

struct _point
{
    int x;
    int y;
};

typedef struct _point POINT;

void main ()
{    
    int  n = 10;

    POINT p1 = {10,20};

 

       printf("%d\n", n);

       printf("%d %d\n", p1.x, p1.y);

}

  1. 10
  2. 10 20

 

 위 형태보다는 아래와 같은 형태를 선호합니다. 많이 사용합니다. 잘 알아두세요.

typedef struct _point
{
    int x;
    int y;
POINT;

void main ()
{    
    int  n = 10;

    POINT p1 = {10,20};

 

       printf("%d\n", n);

       printf("%d %d\n", p1.x, p1.y);

}

  1. 10
  2. 10 20

 

포인터가 필요하다면 여러개를 정의해서 사용할 수 있습니다.

typedef struct _point
{
    int x;
    int y;
POINT, * PPOINT;
void main ()
{
    POINT p1={10,20};
    PPOINT ps = &p1;

 

    printf("%d %d\n", p1.x, p1.y);
    printf("%d %d\n", ps->x, ps->y);
    printf("%d %d\n", (&p1)->x, (&p1)->y);
}

  1. 10 20
  2. 10 20
  3. 10 20

 

꼭! 직접 코드를 만들고 실습해 보세요.

 

3, 전처리

전처리(Preprocess)는 컴파일 전에 처리(변환)되는 코드(작업)를 의미합니다. a3. 프로그램을 참고하세요.

컴파일전에 처리되어 컴파일은 전처리문장을 볼 수 없고 변환된 소스코드만을 볼 수 있습니다.

전처리문장은 모두 #이 붙어 있고 전처리기(Preprocessor)는 이 #이 붙은 문장을 코드를 전처리합니다.

대표적인 전처리 지시자가 #include 입니다. 21. 분할 컴파일 와 22. 라이브러리 만들기를 참고하세요.

우리는 지금까지 코드 라인수를 줄이기 위해 #include를 소스코드에 포함하지 않고 작성해 왔지만 모든 소스코드에는 #include를 포함해야합니다.

우리도 실제  연습예제를 할 때는 모든 소스코드에 포함합니다.

 

C언어 QuickStart1을 할 때 최초로 사용했던 소스를 볼까요?

#include <stdio.h>

void main( )
{
    printf("Hello!");
}

  1. Hello!

지금까지 #include <stdio.h> 문장을 빼고 소스코드를 보였지만 모든 소스코드에는 #include 문장이 있어야합니다. #include 문장은 컴파일러가 소스코드를 번역(기계어)하도록 최소한의 정보를 제공하는 일을 합니다. 자세한 내용은 21. 분할 컴파일 와 22. 라이브러리 만들기를 참고하세요. 이 # 이 붙은 문장은 전처리기가 인식하여 처리하게 되고 # 이 붙은 문장마다 처리해야하는 방법이 다릅니다.

 

아래는 주요 전처리 지시자입니다.

  1. 소스코드확장및 포함

    • #include
  2. 매크로정의및 상수정의

    • #define
  3. 조건부 컴파일

    • #ifdef
    • #ifndef
    • #endif
    • #elif
    • #else
    • #if

 위 내용들을 하나하나 살펴보도록 하겠습니다.

 

1, #include ( 21. 분할 컴파일 참고)

헤더파일을 포함할 때 사용합니다.

  • 기본 디렉토리 헤더를 포함할 때
#include <stdio.h>

지정된 디렉토리를 검색하여 stdio.h파일의 내용을 포함합니다.

 

  • 사용자 헤더를 포함할 때
#include "point.h"

현재작업 디렉토리와 실행파일 디렉토리, 지정된 디렉토리등을 검색하여 point.h파일의 내용을 포함합니다.

 

 

2, #define

기본적으로 상수정의에 사용됩니다.

#define MAX 100
void main( )
{
    printf("%d\n", MAX);
    printf("%d\n", MAX);
    printf("%d\n", MAX);
}
  1. 100
  2. 100
  3. 100

MAX는 define 상수라하며 전처리기에 의해 MAX는 100으로 변환되며 컴파일러는 MAX가 아닌 100을 봅니다.

아래는 컴파일러가 본 위 코드입니다.

void main( )
{
    printf("%d\n", 100);
    printf("%d\n", 100);
    printf("%d\n", 100);
}

 

또 다른 상수예입니다.

#define STR "Hello!"
void main( )
{
    printf("%s\n", STR);
    printf("%s\n", STR);
    printf("%s\n", STR);
}
  1. Hello!
  2. Hello!
  3. Hello!

 STR도 define 상수이며 전처리기에 의해 STR은 "Hello!" 문자열로 변환됩니다.

아래는 컴파일러가 본 위 코드입니다.

void main( )
{
    printf("%s\n""Hello!");
    printf("%s\n""Hello!");
    printf("%s\n""Hello!");
}
  1. Hello!
  2. Hello!
  3. Hello!

 

#define은 함수처럼 정의해서 사용할 수 있습니다. 이를 매크로함수라 합니다. 매크로함수는 함수이기 보다는 전처리기에 의해 코드 자체가 그대로 삽입되는 것입니다.

아래 예는 두 수를 더하는 매크로함수 ADD()의 예입니다.

#define ADD(a,b) a+b
void main( )
{
    printf("%d\n", ADD(2,3));
    printf("%d\n", ADD(5,5));
    printf("%d\n", ADD(10,20));
}
  1. 5
  2. 10
  3. 20

결과는 쉽죠? ADD()매크로 함수는 전처리기에 의해 a+b로 변환되고 컴파일러는 단지, a+b를 볼 뿐입니다.

아래는 컴파일러가 본 위 예제코드입니다.

void main( )
{
    printf("%d\n", 2+3);
    printf("%d\n", 5+5);
    printf("%d\n", 10+20);
}

 

여러문장에 매크로 함수를 정의할 때는 아래와 같이 \를 문장뒤에 입력해야 합니다. #define문장이 끝나지 않았다는 것을 알리는 것입니다.

아래 예제는 0부터 9까지 출력하는 3가지 형태를 보입니다.

#define PRINT( n ) \
{ \
    int i; \
    for( i = 0 ; i < n ; i++) \
        printf("%d\n", i); \
}
void Print(int n)
{
    int i;
    for( i = 0 ; i < n ; i++)
        printf("%d\n", i);
}
void main( )
{
    Print( 10 );   // 1.
    PRINT( 10 );   // 2.
    {              // 3.
        int i; 
        for( i = 0 ; i < 10 ; i++)
            printf("%d\n", i);
    }
}
  1. 0 ~ 9 (생략)
  2. 0 ~ 9 (생략)
  3. 0 ~ 9 (생략)

// 1. 일반 사용자 함수(Print())와 // 2. 매크로 함수(PRINT()) 그리고  // 3. { }블록으로 감싼 코드 매크로함수가 전처리기에 의해 삽입되는 코드를 보입니다.

아래 예제는 컴파일러가 본 위 코드입니다.

void Print(int n)
{
    int i;
    for( i = 0 ; i < n ; i++)
        printf("%d\n", i);
}
void main( )
{
    Print( 10 );   // 1.

   {               // 2.
        int i; 
        for( i = 0 ; i < 10 ; i++) 
            printf("%d\n", i); 
    }
    {              // 3.
        int i;
        for( i = 0 ; i < 10 ; i++)
            printf("%d\n", i);
    }
}

 

매크로 함수를 사용할 때는 주의할 점이 있습니다.

두 정수 중 큰수를 선택하는 예제가 있다면 일반함수로 아래와 같이 만들고

int Max(int a, int b)
{
    if( a > b)
        return a;
    else
        return b;
}
void main( )
{
    int a = 1;
    int b = 2;

    printf("%d\n" , Max( a, b) );
}
  1. 2

매크로 함수로 아래와 같이 만들었다고 한다면

#define Max(a,b) (a > b ? a: b)
void main( )
{
    int a = 1;
    int b = 2;

    printf("%d\n" ,  Max(a,b));
    //printf("%d\n" ,  {a > b ? a: b}); 컴파일러가 본 위문장 코드
}
  1. 2

이때 매크로함수는 문자 그래도를 삽입코드로 만들기 때문에 아래와 같은 실수를 할 수 있습니다.

일반함수를 사용하면 잘 작동합니다.

int Max(int a, int b)
{
    if( a > b)
        return a;
    else
        return b;
}
void main( )
{
    int a = 1;
    int b = 2;

    printf("%d\n" , Max( ++a, ++b) );
}
  1. 3

 하지만 매크로 함수를 사용하면 의도하지 않는 결과가 됩니다.

#define Max(a,b) (a > b ? a: b)
void main( )
{
    int a = 1;
    int b = 2;

    printf("%d\n" ,  Max(++a,++b));
    //printf("%d\n" ,  {++a > ++b ? ++a: ++b}); 컴파일러가 본 위 문장 코드
}
  1. 4

또 두 수를 곱하는 예제를 만든다면..

#define MUL(a, b) a*b
void main( )
{
    printf("%d\n", MUL(1, 2) );
    printf("%d\n", MUL(1+2, 2) );
    printf("%d\n", MUL(1, 2*3) );
} 
  1. 2
    5
    6

 위 경우는 꼭 ()를 사용해야합니다. 아래 처럼요.

#define MUL(a, b) (a)*(b)
void main( )
{
    printf("%d\n", MUL(1, 2) );
    printf("%d\n", MUL(1+2, 2) );
    printf("%d\n", MUL(1, 2*3) );

    //printf("%d\n" , (1)*(2*3) ); 컴파일러가 본 위 문장 코드
}

  1. 2
    6
    6

 

위 내용을 꼭 주의하세요. 그래서 C++에서는 inline이란 키워드를 제공하고 inline함수를 사용하여 매크로함수와 같은 기능을 가지며 위 문제를 해결할 수 있습니다.

 

3, #ifdef, #endif, ...21. 분할 컴파일 와 22. 라이브러리 만들기를 참고하세요.

여기서는 간단한 문법을 공부합니다.

보통 조건부 컴파일이라고 합니다. 어떤 조건에 따라 컴파일러가 인식하거나 인식하지 못하게 할 수 있습니다.

조건부 컴파일 가장 간단한 예제입니다.

#define _DOUBLE_
void main( )
{
#ifndef _DOUBLE_
    int n = 10;

    printf("%d\n", n);
#else
    double d = 2.5;
    printf("%g\n", d);
#endif
}

  1. 2.5

#define _DOUBLE_ 는 그냥 _DOUBLE_ 정의만 하겠다 라는 것입니다.

#ifndef _DOUBLE_ 는 말 그대로 전처리기야 _DOUBLE_ 이 정의되어 있지 않으면

    int n = 10;

     printf("%d\n", n);
위 문장을 남기고

#else 는 아니면

     double d = 2.5;
    printf("%g\n", d);

#endif 는 여기까지 끝

위 문장을 남겨 컴파일러가 인식할 수 있도록 해라 입니다. 컴파일러는 두 문장들 중 하나만 볼 수 있습니다.

컴파일러가 본 위 예제 코드입니다.

void main( )
{

    double d = 2.5;
    printf("%g\n", d);
}

 

또 다른 조건부 컴파일 예제입니다.

#define _INT_
void main( )
{
#ifdef _INT_
    int n = 10;
    printf("%d\n", n);
#else
    double d = 2.5;
    printf("%g\n", d);
#endif
}
  1. 10

#define _INT_ 는 그냥 _INT_ 정의만 하겠다 라는 것입니다.

#ifdef _INT_ 는 말 그대로 전처리기야 _INT_ 이 정의되어 있으면(반대로)

     int n = 10;

     printf("%d\n", n);
위 문장을 남기고

#else 는 아니면

     double d = 2.5;
    printf("%g\n", d);

#endif  여기까지 끝

위 문장을 남겨 컴파일러가 인식할 수 있도록 해라 입니다. 컴파일러는 두 문장들 중 하나만 볼 수 있습니다.

컴파일러가 본 위 예제 코드입니다.

void main( )
{

    int n = 10;

    printf("%d\n", n);
}

 

아래 문장은 여러분이 해석해 보세요.

void main( )
{
#ifdef _INT_
    int n = 10;
    printf("%d\n", n);
#elif defined(_DOUBLE_)
    double d = 2.5;
    printf("%g\n", d);
#else

#endif
}

 

보통 헤더파일에서 사용되며 조건부 컴파일에 사용됩니다.

실제 사용예는 예제 실습에서 보기로 하고 여기서는 정확한 사용법을 알아 두세요.