전처리기(Preprocessor)
전처리기는 컴파일 과정 중 어휘분석 단계 이후에 동작합니다.
프로그램에 직접적으로 속하는 코드라기 보다는, 컴파일러에게 내리는 지시사항과 같습니다.
C/C++에서 전처리 코드는 #으로 시작합니다.
#include
가장 익숙한 #include 는 다음에 지정하는 파일을 이 소스코드를 컴파일하기 전에 읽어들이라는 지시입니다.
괄호의 형태로는 환경변수에 지정된 경로, 따옴표의 형태는 현재 코드가 위치한 곳으로 부터의 상대주소로 파일을 지시합니다.
<>로 엮을 경우 환경 변수 경로로부터 이 파일을 찾게 되며,
""로 엮을경우 현재 이 소스코드가 위치한 경로부터 헤더를 찾게 됩니다.
#include <stdio.h>
#include "main.h"
#define / #undef
#define은 특정한 단어 혹은 수식을 치환하는 정의식입니다.
#define A B의 형태로 사용되며, A를 B로 치환할 것을 지시합니다.
#define MAXIMUM 300 이라고 하면
이 코드에 등장하는 MAXIMUM 이라는 이름은 모두 300이라는 숫자값으로 치환됩니다.
조금 더 복잡한 흔히 매크로 함수라 하는 함수형태의 치환 방법도 존재합니다.
#define Plus(A,B) A+B
int result = Plus(1,2) 라고 사용했을때,
이는 int result = 1+2로 치환되어 result 에는 3이라는 값이 대입됩니다.
함수의 형태처럼 인수를 만들면 그 인수가 해당하는 부분에 치환되는 방식이라고 할 수 있습니다.
- 단순한 치환식이기 때문에 매크로 함수의 인수는 자료형의 구분을 하지 않습니다.
- 함수를 실제 사용한 지점에서 치환된 결과물이 문법적 오류가 있다면 컴파일 과정에서 에러로 표기될 것입니다.
#undef는 #define으로 정의한 부분을 해제할 때에 사용합니다.
#define MAXIMUM 20
#define BYTE char
#define Plus(A,B) A + B
#undef MAXIMUM
이를 잘 이용하면 반복되는 코드를 #define과 #undef을 이용한 헤더파일 처리를 통하여 줄이는 것도 가능합니다.
#ifdef
#ifdef와 #endif, 그리고 #else #elif는 함께 사용되는 조건부 컴파일 지시자입니다.
#ifdef \_\_WIN32
void Init() {}
#endif
조건부 컴파일 지시자가 나오기 전까지 __WIN32가 정의 되어 있으면, void Init(){}부분을 컴파일 하며, 그렇지 않다면 무시합니다.
- #define __WIN32의 형태로 정의 되어 있어야 합니다.
#ifdef \_\_WIN32
void Init() { OS = \_WIN32;}
#elif \_\_LINUX
void Init() { OS = \_LINUX;}
#else
void Init() { OS = \_FAIL;})
#endif
#elif와 #else는 #ifdef 다음에 추가로 조건을 걸거나 혹은 나머지에 대해 전부 처리할 사항을 기입합니다.
조건부 컴파일문은 위의 예제와 같이 공통의 선언부에 구현부를 따로 만들때 주로 이용됩니다.
예를 들어, print 라는 함수를 만들면 그 원형은 전부 같겠지만, 플랫폼에 따라 지원되는 함수들이 다를 수 있으므로 그 구현방식이 달라질 수 있습니다.
선언부는 공통적으로 유지하되, 구현부를 조건부 컴파일로 나누어 놓으면, 프로그램의 큰 틀은 유지하면서 여러 플랫폼에서 동작하는 소스코드를 작성할 수 있습니다.
#error
#error는 컴파일러에게 의도적인 에러를 발생시키도록 합니다.
주로 #ifdef ~ #endif와 함께 사용되며, 특정한 함수나 기능을 지원하지 않는 경우, 컴파일을 하지 못하도록 하며, 그 이유를 설명할때 사용하게 됩니다.
#ifdef \_\_WIN32
void Init() { OS = \_WIN32;}
#elif \_\_LINUX
void Init() { OS = \_LINUX;}
#else
#error UnknownPlatform
#endif
위의 코드는 WIN32나 LINUX 가 아니면 UnknownPlatform 이라는 컴파일 에러를 발생시킵니다.
#line
#line은 기본 정의인 __FILE__과 __LINE__을 재 정의하도록 합니다. 자주 사용되지는 않습니다.
- __FILE__은 소스코드의 이름 매크로
- __LINE__은 소스코드 내 현재 라인수를 표기하는 매크로
#line A B의 형태로 사용하며, A는 __LINE__의 값을, B는 __FILE__의 값을 변경합니다.
- #line 2 "MainFunc"와 같은 방식입니다.
#pragma
#pragma는 일종의 확장 지시자입니다.
각 회사의 컴파일러별로 C/C++표준에 속하지 않는 독자적인 지원을 하는 경우가 있으며, #pragma로 컴파일러에 지시를 내립니다.
#pragma once
#pragma warning
#pragma once 는 헤더파일에 사용하며, 이 헤더파일을 한번만 부르도록 제어하는 지시자입니다.
이는 MS VisualStudio 에서는 잘 동작하지만, GCC 에서는 경고를 출력합니다.
- GCC 도 지원은 하지만 사용하지 않을 것을 권고 하고 있습니다.