C PreProcessor
프로그래머가 작성한 C 소스코드에는 전처리기 지시자 ( directives ), 사용자 정의 매크로가 포함되어 있기 때문에 먼저 전처리 과정을 거친 후에 최종 결과가 컴파일러에 전달됩니다.
파일을 include 할 때 사용하는
#include
매크로를 정의하거나 해제할 때 사용하는
#define
#undef
선택적으로 특정 코드를 제외하거나 포함할 때 사용하는
#if(n)def
#if
#else
#elif
#endif
Diagnostic 메시지와 flow control 에 사용되는
#error
#warning
Line control 에 사용되는
#line
일종의 확장 지시자
#pragma
전처리기 지시자는 #
문자로 시작하는데 사이에 공백을 여러개 두어도 됩니다.
# if defined(__BIG_ENDIAN) // #if
# define AFMT_S16_NE AFMT_S16_BE // #define
# elif defined(__LITTLE_ENDIAN) // #elif
# define AFMT_S16_NE AFMT_S16_LE // #define
# else // #else
# error "could not determine byte order" // #error
# endif // #endif
C++ 템플릿도 확장 결과가 소스코드가 된다는 점에서 보면 매크로와 비슷한데
템플릿은 처리 과정에서 syntatic, semantic 체크도 하는 반면에 매크로는 단순 lexical 치환입니다.
그렇다고 매크로가 C/C++ 문법에서 완전히 자유로운 것은 아니고 가령
quotes 을 사용할 경우는 "
or '
의 balance 가 맞아야 하고
##
연산자를 이용해 두 token 을 결합할 경우는 결과가 적합한 전처리기 token 이 되어야 합니다.
예를 들어
=
와+
를 결합하면=+
가 되는데 이것은 적합한 전처리기 token 이 아닙니다.
매크로와 비슷한 기능으로 enum 이 있는데 enum 은 compile-time 에 처리되는 기능으로 사용자 타입도 지정할 수 있고, scope 도 가질 수 있습니다. 원래 C 언어에서는 매크로만 사용하였는데 나중에 enum 이 추가되었다고 합니다.
매크로의 특징
매크로의 특징은 단순 텍스트 프로세싱이라는 점입니다. 예를 들어 프로그래밍 언어에서는 NUM 이라는 변수에 10 을 대입한 후에 INC ( increment ) 하면 11 이 되지만 매크로에서는 INC 한 결과를 얻으려면 다음과 같이 직접 모든 결과값에 대해 매크로 정의를 해주어야 합니다. ( 매크로를 다룰 때는 약간 발상의 전환이 필요합니다. )
$ gcpp $ gcpp
#define JOIN(x, y) x ## y #define JOIN(x, y) x ## y
#define INC(x) JOIN(INC_, x) #define DEC(x) JOIN(DEC_, x)
#define INC_0 1 #define DEC_1 0
#define INC_1 2 #define DEC_2 1
#define INC_2 3 #define DEC_3 2 // 직접 모든 결과값에 대해
#define INC_3 4 #define DEC_4 3 // 매크로 정의를 해주어야 한다.
#define INC_4 5 #define DEC_5 4
INC(3) DEC(3)
@ @
4 // 결과 2 // 결과
-------------------------------------------------------
1. INC(3) ----> JOIN(INC_, 3)
2. JOIN(INC_, 3) ----> INC_3
3. INC_3 ----> 4
매크로 테스트에 사용되는 gcpp 명령
본문 전체에 걸쳐 매크로 테스트에 gcpp
명령이 사용되는데 이것은 다음과 같은
bash shell alias 입니다.
$ alias gcpp='gcc -E -P - <<\@'
# -P 옵션을 사용하면 매크로 확장시 다음과 같은 linemarkers 가 출력되지 않습니다.
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
. . .