이 글에서는 C 또는 C++에서 전처리기(Preprocessor)가 어떻게 작동하는지 자세히 살펴보겠습니다.
전처리기란 실제 컴파일이 시작되기 이전 단계에서 컴파일러에게 정보를 처리하도록 지시하는 명령어입니다. 즉, 소스 코드가 본격적으로 컴파일되기 전에 필요한 사전 작업을 수행하는 역할을 담당합니다.
전처리기 지시문의 기본 규칙
모든 전처리기 지시문은 # 기호로 시작하며, 한 줄에서 지시문 앞에는 공백 문자만 올 수 있습니다. 또한 전처리기 지시문은 C++ 문장이 아니기 때문에 세미콜론(;)으로 끝나지 않는다는 점을 기억해야 합니다.
지금까지의 예제 코드에서 이미 #include 지시문을 보셨을 것입니다. 이 지시문은 헤더 파일을 소스 파일에 포함시키는 매크로로 사용됩니다.
C++에서 지원하는 대표적인 전처리기 지시문으로는 #include, #define, #if, #else, #line 등이 있습니다. 그중에서 가장 중요하게 사용되는 지시문들을 하나씩 알아보겠습니다.
#define 전처리기
#define 지시문은 심볼릭 상수(symbolic constant)를 생성합니다. 이렇게 만들어진 심볼릭 상수를 매크로(macro)라고 부르며, 일반적인 작성 형식은 다음과 같습니다.
#define macro-name replacement-text
예제 코드
#include <iostream>
using namespace std;
#define PI 3.14159
int main () {
cout << "Value of PI :" << PI << endl;
return 0;
}실행 결과
Value of PI :3.14159
조건부 컴파일(Conditional Compilation)
여러 전처리기 지시문을 활용하면 프로그램 소스 코드 중 선택적인 부분만 컴파일할 수 있습니다. 이러한 과정을 조건부 컴파일이라고 합니다.
조건부 전처리기 구조는 일반적인 'if' 선택 구문과 매우 유사한 방식으로 동작합니다. 다음 코드를 살펴보겠습니다.
#ifndef NULL #define NULL 0 #endif
위 코드는 NULL이 아직 정의되지 않은 경우에만 0으로 정의하겠다는 의미입니다. 이런 방식은 헤더 파일의 중복 포함을 방지할 때도 유용하게 활용됩니다.
디버깅용 매크로 활용
조건부 컴파일은 디버깅 목적으로 프로그램을 빌드할 때 특히 유용합니다. 하나의 매크로 정의만으로 디버깅 코드를 켜거나 끌 수 있습니다.
#ifdef DEBUG cerr <<"Variable x = " << x << endl; #endif
위 코드에서 DEBUG라는 심볼릭 상수가 #ifdef DEBUG 지시문 이전에 정의되어 있다면, 해당 cerr 문이 컴파일에 포함됩니다. 정의되어 있지 않다면 이 코드는 완전히 무시됩니다.
또한 #if 0 구문을 사용하면 프로그램의 특정 부분을 통째로 주석 처리한 것과 같은 효과를 낼 수 있습니다.
#if 0 컴파일되지 않는 코드 영역 #endif
종합 예제 코드
#include <iostream>
using namespace std;
#define DEBUG
#define MIN(a,b) (((a)<(b)) ? a : b)
int main () {
int i, j;
i = 100;
j = 30;
#ifdef DEBUG
cerr <<"Trace: Inside main function" << endl;
#endif
#if 0
/* 이 부분은 주석 처리된 영역입니다 */
cout << MKSTR(HELLO C++) << endl;
#endif
cout <<"The minimum is " << MIN(i, j) << endl;
#ifdef DEBUG
cerr <<"Trace: Coming out of main function" << endl;
#endif
return 0;
}실행 결과
Trace: Inside main function The minimum is 30 Trace: Coming out of main function
실행 결과를 보면 DEBUG가 정의되어 있어 추적(trace) 메시지가 출력되고, 함수형 매크로 MIN(a,b)가 정상적으로 두 값 중 작은 값인 30을 반환한 것을 확인할 수 있습니다. 반면 #if 0 블록 안의 코드는 컴파일되지 않아 출력에 나타나지 않습니다.
정리
전처리기는 컴파일 이전 단계에서 소스 코드를 가공하는 강력한 도구입니다. #define으로 상수와 함수형 매크로를 정의하고, #ifdef, #ifndef, #if 같은 조건부 지시문으로 플랫폼별 코드 분기나 디버깅 코드 관리를 효율적으로 할 수 있습니다. 이러한 전처리기를 잘 활용하면 코드의 유지보수성과 이식성을 크게 높일 수 있습니다.