C 전처리기

매크로는 컴퍼일러에게 코드의 특성을 알려주는 키워드이다. 따라서 기계어로 컴파일 과정에서 필요한 요소이고, 매크로 자체가 기계어 코드로 생성되지는 않지만 특정 코드를 제어하는데 사용한다. 특정 프로그램 파일을 현재 위치에 첨부하여 하나의 파일처럼 컴파일한다. 보통 IDE 개발툴들은 보통 프로젝트(Project)라는 개념이 도입되어 있다. 한개의 프로젝트를 수행하기 위한 프로그램 파일 및 설정 등을 관리하고, 컴파일하여 실행 파일을 만들고 디버깅을 제공한다. 여러개의 파일을 프로젝트에 등록만 하면 서로 설정된 함수나 변수를 사용할 수 있다. 프로그램 파일이 개별적으로 저장되고 개별적으로 컴파일되어 오브젝트 파일을 생성한다. 서로 다른 함수의 함수나 기타를 활용하기 위해 함수형이나 변수형이나 선언 내용을 다른 파일 끼리 연결하는 방식이 필요하다. 이때 #include을 사용한다. #include <stdio.h> int main(void) { printf("Hello, world!\n"); return 0; } printf함수가 stdio.h 파일에 정의되어 있다. 따라서 printf함수의 모양을 알 수 있다. 만약 함수의 모양을 모른다면 호출이 불가능해진다. 매크로 #if, #ifdef, #ifndef, #else, #elif, #endif는 조건부 컴파일하도록 정의한다. 따라서 조건이 맞지 않으면 컴파일에서 제거 된다. 즉, 조건이 맞지 않으면 소스 자체가 없는 것과 같은 효과가 있다. #if VERBOSE >= 2 print("trace message"); #endif VERBOSE가 정의 된 값에 따라 print 함수를 제거할 수 있다. 조건 판단은 C언어 조건과 같이 예에서 VERBOSE가 2보다 크거나 같으면 컴파일한다. 이 경우 VERBOSE 값이 미리 정의되어 있어야 조건에서 참이 가능하다. 프로그램 코드 작성 시, 임의 영역을 다양한 상황에 맞게 컴파일에서 제거할 수 있다. #ifdef __unix__ /* __unix__ is usually defined by compilers targeting Unix systems */ # include <unistd.h> #elif defined _WIN32 /* _Win32 is usually defined by compilers targeting 32 or 64 bit Windows systems */ # include <windows.h> #endif 이 경우 OS가 UNIX 또는 윈도우 환경에 따라 다른 파일을 포함 시킬 수 있다. __unix__가 정의되어 있다면, unistd.h 파일을 첨부하고 그렇지 않고 _WIN32가 정의되어 있다면 windows.h 파일을 첨부한다. __unix__나 _WIN32의 정의는 다양한 방식으로 정의 할 수 있다. 매크로 #if와 defined와 연결하면 다음과 같이 복잡한 조건부 컴파일이 가능하다. #if !(defined __LP64__ || defined __LLP64__) || defined _WIN32 && !defined _WIN64 // we are compiling for a 32-bit system #else // we are compiling for a 64-bit system #endif #error을 다음과 사용하여 컴파일 실패하게 만들 수 있다. #if RUBY_VERSION == 190 # error 1.9.0 not supported #endif #define은 특정 숫자, 함수, 프로그램 블록을 다른 형태로 변환 지정한다. 다양한 선언이 가능하다. 조건은 선언 된 그 줄에 만 적용된다. 다음 줄로 연결하려면 '\'을 사용하여 한 줄처럼 코딩하면 된다. 프로그램 파일 내에서 #define을 사용하는 것이 일반적이지만 makefile이나 기타 IDE 툴의 설정을 통해서도 정의 할 수 있다. 따라서 컴파일되는 파일에 없다고 무조건 선언이 되지 않았다는 보장이 없다. 남의 프로그램을 사용할 때는 주의가 필요하다. 이렇게 정의 내용을 해제하려면 #undef을 사용하면 정의 된것이 없었던 것이 된다. 만약 어떤식으로든 이미 특정 정의가 선언되어 있다면 다시 설정할 때, #undef을 이용해 제거하고 다시 설정하면 된다. 매크로 선언은 대상변환형(Object-like macros)과 유사함수변환형(function-like macros) 가 있다. 대상변환형은 인수가 없고, 유사함수변환형은 함수와 유사하게 정의 : #define <identifier> <replacement token list> // object-like macro #define <identifier>(<parameter list>) <replacement token list> // function-like macro, note parameters 정의 된 매크로는 "#undef"을 사용하여 무효화 : #undef <identifier> // delete the macro 대상변환형 : #define PI 3.14159 코딩 중 PI 토큰이 나오면 바로 3.14159로 바꾸어 컴파일한다. 단순한 문자 교환이 이루어지는 것이다. 이렇게 하면 장점은 특정 숫자의 의미가 정확히 와닿지 않는 것을 방지할 수 있다. #define BUFFER_SIZE 1024 foo = (char *) malloc (BUFFER_SIZE); for (int count = 0;count < BUFFER_SIZE;count++) // ... 코드 내에 정의 된 BUFFER_SIZE는 1024 바꾸어 컴파일한다. 이것은 다음과 같이 코딩한 것과 같다 : foo = (char *) malloc (1024); for (int count = 0;count < 1024;count++) // ... 코딩이 많아 질 수록 1024가 갖는 의미가 혼돈 스러울 수가 있다. #define NUMBERS 1, \ 2, \ 3 int x[] = { NUMBERS }; // ==> int x[] = { 1, 2, 3 }; 와 같다. #define 한 줄에 정의된 것만 취급한다. 다음 줄에 계속 연결할 때는 위와 같이 \ 을 사용한다. foo = X; #define X 4 bar = X; 매크로 정의는 정의된 이후부터 적용된다. 따라서 위 코드는 : foo = X; bar = 4; 토큰 교환이 bar 변수에만 적용된다. 유사함수변환형: #define RADTODEG(x) ((x) * 57.29578) 토큰 연결 연산자는 두개의 토큰을 하나의 토큰으로 연결한다. 예를 들어: #define DECLARE_STRUCT_TYPE(name) typedef struct name##_s name##_t DECLARE_STRUCT_TYPE(g_object); // 출력 결과는 typedef struct g_object_s g_object_t; 컴파일러에게 특성 옵션이나 라이브러리 형태등을 지정할 수 있다. 다양하므로 한마디로 정의 할 수 없다. 여러 가지 환경에 적응할 수 있도록 컴파일러 마다 그리고 CPU 및 시스템 특성에 맞는 것들이 추가되거나 삭제된다. C 언어의 문법 “Object-like macros”. 2012년 12월 13일에 원본 문서에서 보존된 문서. 2012년 11월 25일에 확인함.  “Function-like macros”. 2012년 12월 13일에 원본 문서에서 보존된 문서. 2012년 11월 25일에 확인함.  (영어) Macros