조건부 포함
조건부 포함 (Conditional inclusion)
같은 소스 파일을 여러 환경에서 재사용하려면 "이 환경에서는 이 조각을 컴파일하고, 저 환경에서는 저 조각을" 같은 선택이 필요해요. 전처리기가 그 선택을 담당하는데, 그 중심에 서 있는 지시자가 #if·#ifdef 계열이에요. 소스의 일부를 조건에 따라 넣거나 빼는 이 기능을 '조건부 포함'이라고 불러요.
출처: cppreference
본문
문법 (Syntax)
#if expression
#ifdef identifier
#ifndef identifier
#elif expression
#elifdef identifier // (since C23)
#elifndef identifier // (since C23)
#else
#endif
설명 (Explanation)
조건부 전처리 블록은 #if, #ifdef, #ifndef 지시자로 시작해요. 그 뒤에는 원하는 만큼의 #elif, #elifdef, #elifndef(C23) 지시자, 선택적으로 최대 하나의 #else 지시자가 오고, 마지막에 #endif 지시자로 끝나요. 블록 안에 있는 중첩된 조건부 전처리 블록은 각각 따로 처리돼요.
#if, #ifdef, #ifndef 지시자는 지정된 조건을 검사하고, 그 값이 참이면 해당 코드 블록을 컴파일해요. 그 경우 뒤따르는 #else, #elifdef, #elifndef(C23), #elif 지시자는 무시돼요. 반대로 조건이 거짓이면 해당 블록은 건너뛰고, 뒤따르는 #else, #elifdef, #elifndef(C23), #elif 지시자 중 하나가 처리돼요.
- 뒤따르는 지시자가
#else면,#else가 제어하는 코드 블록은 조건 없이 컴파일돼요. - 뒤따르는 지시자가
#elif,#elifdef,#elifndef(C23)면, 마치#if지시자인 것처럼 동작해요. 조건을 검사해 그 블록을 컴파일하거나 건너뛰고, 건너뛴 경우에는 다시 뒤따르는#elif,#elifdef,#elifndef(C23),#else지시자를 처리해요.
조건부 전처리 블록은 #endif 지시자로 끝나요.
조건 평가 (Conditional evaluation)
#if, #elif
표현식은 상수와 #define 지시자로 정의된 식별자만 사용하는 상수 표현식이에요. 리터럴도 아니고 #define으로 정의되지도 않은 식별자는 0으로 평가되며, true만 1로 평가돼요(C23).
표현식에는 defined 식별자 또는 defined (identifier) 형태의 단항 연산자가 들어갈 수 있어요. 이들은 식별자가 #define으로 정의되어 있으면 1, 아니면 0을 돌려줘요. 이 문맥에서 __has_include, __has_embed, __has_c_attribute는 정의된 매크로 이름처럼 취급돼요(C23). 표현식이 0이 아닌 값으로 평가되면 그 코드 블록이 포함되고, 아니면 건너뛰어져요. 사용된 식별자가 상수가 아니면 0으로 치환돼요.
전처리 지시자 문맥에서 __has_c_attribute 표현식은 주어진 속성 토큰이 지원되는지, 지원 버전이 무엇인지 검사해요. 자세한 내용은 속성 검사를 참고하세요. (C23)
참고: DR 412 이전에는 #if _cond1_ ... #elif _cond2_가 #if _cond1_ ... #else 다음에 #if _cond3_이 오는 형태와 달랐어요. 전자에서는 _cond1_이 참이면 두 번째 #if는 건너뛰어지므로 _cond3_이 잘 형성될 필요가 없지만, #elif의 _cond2_는 유효한 표현식이어야 했죠. DR 412 이후에는 건너뛰어진 코드 블록을 이끄는 #elif도 함께 건너뛰어져요.
결합 지시자 (Combined directives)
식별자가 매크로 이름으로 정의되었는지 검사해요.
#ifdef식별자는 본질적으로#if defined식별자와 같아요.#ifndef식별자는 본질적으로#if !defined식별자와 같아요.#elifdef식별자는 본질적으로#elif defined식별자와 같아요.#elifndef식별자는 본질적으로#elif !defined식별자와 같아요. (C23)
주의할 점 (Notes)
#elifdef와 #elifndef 지시자는 C23을 대상으로 하지만, 구현들이 이를 **적합 확장(conforming extension)**으로 더 오래된 언어 모드에도 역이식(backport)할 수 있어요.
예제 (Example)
매크로 정의 여부와 값을 조합해 서로 다른 분기를 선택하는 예시예요.
#define ABCD 2
#include <stdio.h>
int main(void)
{
#ifdef ABCD
printf("1: yes\n");
#else
printf("1: no\n");
#endif
#ifndef ABCD
printf("2: no1\n");
#elif ABCD == 2
printf("2: yes\n");
#else
printf("2: no2\n");
#endif
#if !defined(DCBA) && (ABCD < 2 * 4 - 3)
printf("3: yes\n");
#endif
// C23 directives #elifdef/#elifndef
#ifdef CPU
printf("4: no1\n");
#elifdef GPU
printf("4: no2\n");
#elifndef RAM
printf("4: yes\n"); // selected in C23 mode, may be selected in pre-C23 mode
#else
printf("4: no3\n"); // may be selected in pre-C23 mode
#endif
}
가능한 출력:
1: yes
2: yes
3: yes
4: yes
같이 보기 (See also)
#define/#undef— 매크로 정의·해제#error/#warning— 진단 지시자