2010. 5. 5. 16:54

 

 1. 매크로 기본

C언어에서 매크로는 컴파일러가 컴파일을 하기 전에 전처리기에 의해 처리된다. #으로 시작하며 #define, #include 등이 있다.  다음은 주로 많이 사용하는 매크로들이다.

Macro

Description

#define, #undef 매크로 상수나 매크로 함수를 정의(해제) 할 때 사용
#include 파일포함

#ifdef,#ifndef,

#elif,#else,#endif

조건부 컴파일
#pragma 컴파일러나 Linker에게 지시할 때 사용
#line 컴파일할 때 출력하는 Line을 변경한다.
#error 에러를 출력하고 컴파일러를 중지시킨다.

 

2. 매크로 상수와 매크로 함수.

#define 문을 사용하면 매크로 함수와 매크로 상수를 만들수 있다.

#include <stdio.h>

#define MAX 10
#define SQUARE(x) (x)*(x)

void main()
{
    int ar[MAX];                 // int ar[10]
    int s = SQUARE(5);       // int s = (5)*(5)

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

 

매크로 함수는 잘못 사용할 경우 버그를 유발할 수 있으므로 조심해야한다.

#include <stdio.h>

#define SQUARE(x) (x)*(x)

void main()
{
    int x = 3;
   
 int s = SQUARE(++x);  // (++x)*(++x)

    printf("s = %d\n", s);     // s = 25
}

위의 경우 프로그래머는 16을 예상하지만 결국 x는 2번증가 되어 25가 나올 것이다.

 

매크로 함수는 모든 형에 적용할 때 사용하는 함수를 만들수 있어 유용하다.

#include <stdio.h>

#define max(a,b) ( (a) > (b) ) ? (a):(b)       // 1

//int max(int a, int b) { return a>b ? a:b;}    // 2

void main()
{
    printf("%d\n", max(5,3) );
    printf("%g\n", max(5.4,3.2) );
}

2번은 int 이외의 값이 전달될 경우, 엉뚱한 결과를 출력할 것이다. 하지만 1번은 모든 type에 대해 제대로 동작할 것이다. (물론 ++ 연산자를 포함한 식이 전달될 경우는 엉뚱한 결과가 나올 것이다.)

 

매크로의 정의가 2줄이상을 경우는 '\'기호를 사용해서 연결해야 한다.

#include <stdio.h>

#define assert(x) if (!(x) ) \
                  {          \
                     printf("assert failed. %s(%d)\n", __FILE__, __LINE__); \
                  }

void main()
{
    int n;
    scanf("%d",&n);
    assert(n);
}

 

3. #, ##을 이용한 매크로 정의

#을 사용하면 해당 심볼을 문자형태로 출력할 수 있다.

#include <stdio.h>

#define dprint(expr) printf(#expr " = %g\n", expr )

void main()
{
    double x = 3.4;
    double y = 2.0;
    dprint(x/y);    // printf("x/y" " = %g\n", expr)
}

위의 예에서 dprint #은 x/y을 "x/y"형의 문자열로 바꾸어 주는 역할을 한다.

 

##은 2개의 Token을 연결하는데 사용한다.

#include <stdio.h>

#define tprint(expr) printf("%d, %d\n", expr##1, expr##2)

void main()
{
    int a1 = 10, a2 = 20;
    tprint(a);

}

##앞의 symbol과 ##뒤의 symbol이 여백없이 연결이 된다.

 

4. 조건부 컴파일 기능

#ifdef, #ifndef, #if, #elif, #else, #endif 등은 조건부 컴파일 기능에 사용된다. 아래 코드를 실행해 보자.

#include <stdio.h>

#define DEBUG     // 주석처리했을 때와 그렇지 않을 때를 비교해 보자.

void main()
{
#ifdef DEBUG
    printf("Debug Message\n");
#endif

    printf("Good-Bye! ^^\n");
}

위 코드는 아래와 같은 방법으로도 사용될 수 있다.

#include <stdio.h>

#define DEBUG

#ifdef DEBUG
    #define TRACE(x) printf(x)
#else
    #define TRACE(x)
#endif

void main()
{
    TRACE("Debug Message\n");
    printf("Good-Bye! ^^\n");
}

또한 아래와 같이 System에 따라 다른 헤더를 포함시킬때도 사용할 수 있다.

#if SYSTEM == WINNT
    #define HDR "winnt.h"

#elif SYSTEM == LINUX
    #define HDR "linux.h"

#elif SYSTEM == MAC
    #define HDR "mac.h"

#endif

#include HDR

 

5. Conditional Inclusion

프로그램을 만들때 여러개의 헤더와 소스 파일을 사용하다 보면 특정 헤더파일이 2번 include되는 경우가 발생할 수 있다. 이 경우 컴파일시에 문제가 발생할 수 있다.

/* hdr.h */

struct pepople
{
    char name[256];
    int  age;
};

/* xxx.c */

#include "hdr.h"
#include "hdr.h"

void main()
{

}

위의 경우 강제로 2번 include 했지만 실제 작업중에는 피치 못하게 2번 include하게 되는 일이 발생할 수 있다. 이때 헤더 파일이 1번만 include되게 하려면 다음과 같이 하면된다.

/* hdr.h */

#if !defined(HDR) // #ifndef HDR 과 동일

#define HDR

struct pepople
{
    char name[256];
    int  age;
};

#endif /* HDR */

/* xxx.c */

#include "hdr.h"
#include "hdr.h"

void main()
{

}

모든 헤더파일에는 위와 같은 매크로를 넣어주는 것이 일반적이다.

 

6. 미리 정의된 매크로

C 에는 사용자가 정의 하지 않더라도 미리정의되어 있는 매크로 상수가 몇가지 있다.

Predefine Macro

Description

__FILE__

현재 컴파일되는 파일의 이름

__LINE__

현재 컴파일되는 소스의 Line No.

__DATE__

현재 날짜

__STDC__

현재 컴파일러가 ANSI 표준을 따를 경우 1로 정의 된다.

__TIME__

시간

__TIMESTAMP__

 
아래 코드를 보자.

#include <stdio.h>

void main()
{
    printf("%s\n", __FILE__);
    printf("%d\n", __LINE__);
    printf("%s\n", __DATE__);
    printf("%s\n", __TIME__);
    printf("%d\n", __TIMESTAMP__);
}

 

7. #pragma

#pragma 매크로는 컴파일러나 Linker에게 어떤 지시를 하기 위해 사용한다.

     #pragma 지시어

형식으로 사용된다. 지시어의 종류는 컴파일러 다르지만 vc++에서 사용되는 지시어는 대략 아래와 같은 것들이 있다.

alloc_text auto_inline bss_seg check_stack
code_seg comment component conform
const_seg data_seg deprecated function
hdrstop include_alias init_seg1 inline_depth
inline_recursion intrinsic managed message
once optimize pack pointers_to_members1
pop_macro push_macro runtime_checks section
setlocale unmanaged vtordisp1 warning
message 라는 지시어를 사용하면 컴파일중 Output 창에 메시지를 출력하는 것이 가능하다.

void main()
{

#pragma message("이 메세지 컴파일러가 컴파일하는 중에 출력창에 출력됩니다.")

}

# 매크로와 #pragma, __FILE__, __LINE__ 등을 응용하면 아래와 같이 유용한 매크로를 정의해서 사용할 수 있다.

#define TOSTR(x)    #x
#define STR(x)      TOSTR(x)
#define MSG(desc)   message(__FILE__"("STR(__LINE__)"):"#desc)

void main()
{
    int x = 10;
    int y = 20;

#pragma MSG(이 코드는 나중에 다시 살펴볼 필요가 있습니다)  // A

    x = y / 2;

    ......;
    ......;
}

위의 A처럼 코드를 만들어 둘 경우 매번 컴파일 할 때마다 메시지를 볼수 있을 것이다.
프로그램이 길어지고 나중에 변경할 필요가 있을 때 잊지 않고 작업을 할 수 있을 것이다.

 

8. #line, #error

#line매크로는 컴파일러가 출력하는 현재 line count를 변경하는 매크로 이다. 자주 사용되지는 않는다.

void main()
{
#line 100

    int x = 10      // error ;를 빼 먹었다.
}

위 코드를 컴파일 하면 ;이 빠져서 에러가 날것이다. 그런데 101번째 line이라고 나오게 될것이다.

 

#error 매크로는 컴파일을 멈추고 에러를 출력하게 하는 매크로 이다.

// #define LINUX

void main()
{
#ifndef LINUX
    #error this os is not linux
#endif
}

Posted by devanix