18-2-다.#if

#ifdef, #ifndef는 매크로의 존재 여부만으로 컴파일 조건을 판단하며 매크로가 어떤 값으로 정의되어 있는지는 평가하지 않는다. 이에 비해 #if는 매크로의 값을 평가하거나 여러 가지 조건을 결합하여 컴파일 여부를 결정하는 좀 더 복잡한 전처리문이다. #ifdef보다는 사용법이 조금 까다롭지만 C 언어의 조건문과 유사하므로 쉽게 익힐 수 있다. 기본 형식은 다음과 같다.
#if 조건1
코드1 // 조건1을 만족하면 코드1을 컴파일
#elif 조건2
코드2 // 조건 2가 만족되면 코드2를 컴파일
#else
코드3 // 둘 다 맞지 않으면 코드 3을 컴파일
#endif
C 언어의 if .... else if .... else와 거의 유사한 구문이라고 볼 수 있다. #elif와 #else는 필요없을 경우 생략 가능하며 #elif는 얼마든지 올 수 있다. #elif를 반복적으로 계속 사용하면 C언어의 switch case 구문과 유사한 구조를 만들 수 있다. #if는 뒤의 조건을 평가해 보고 이 조건이 참이면 바로 아래의 블록을 컴파일러에게 넘기고 그렇지 않다면 삭제하여 없는 것으로 취급한다. 뜻 그대로 조건에 따라 컴파일할 블록을 선택하는 것이다.
#if와 #elif다음에는 컴파일 여부를 결정하는 조건문이 오는데 이 조건문은 전처리 과정에서 진위 여부를 판단할 수 있는 것이어야 한다. 실행 중에 값이 결정되는 변수를 참조한다거나 함수를 호출하는 것은 안되며 주로 매크로의 값이 평가 대상이다. 다음은 #if 전처리문의 작성 규칙인데 대부분 상식과 일치하므로 쉽게 이해할 수 있을 것이다.
매크로값을 비교할 때는 상등, 비교 연산자를 사용한다. 같다, 다르다는 ==, != 연산자를 사용하며 대소를 비교할 때는 >, <, >=, <= 비교 연산자가 사용된다. 구형 컴파일러들은 상등 연산자만 허용했었으나 최신 컴파일러는 비교 연산자도 사용할 수 있다. C언어의 상등, 비교 연산자와 모양이 완전히 일치하는 셈이다.
#if (LEVEL == 3)
#if (VER >= 7)
조건문은 꼭 괄호로 싸지 않아도 상관없지만 C의 조건문에 익숙한 개발자들은 #if에도 가급적 괄호를 붙여 주는 편이며 괄호가 있는 편이 보기에도 안정감이 느껴져 좋다. #if는 조건문이 참일 때 1로, 거짓일 때 0으로 평가하는데 결과가 0이 아니면 이어지는 코드 블록을 컴파일한다. 이 점도 C와 동일하다.
비교 대상은 정수 상수여야 하며 실수나 문자열은 매크로와 비교할 수 없다. 컴파일러에 따라 실수 비교를 허용하는 것들도 있는데 조건부 컴파일을 통제하는 매크로는 대소가 있는 값이라기 보다는 주로 표식이기 때문에 실수는 별로 실용성이 없다고 할 수 있다. 정수값을 가지는 다른 매크로와 값을 비교하는 것은 가능하다.
#if (VER == 3.14) // 에러
#if (NAME == "Kim") // 에러
#if (LEVEL == BASIC) // 가능
버전 번호 같은 경우에 1.0, 1.5같이 실수로 표기하지만 매크로 상수로 버전을 표시할 때는 100, 150 등과 같이 정수화해서 사용하는 것이 일반적이다.
수식 내에서 간단한 사칙 연산을 할 수 있다. 전처리기가 연산문을 평가한 후 그 결과를 비교하므로 다소 복잡한 식은 굳이 결과를 계산해 넣을 필요없이 수식을 바로 써도 상관없다.
#if (LEVEL*2 == 6)
#if (TIME == 365*24)
나머지 연산, 비트 연산 등도 가능하다. 그러나 ++, --, 포인터 연산, sizeof, 캐스트 연산 등은 사용할 수 없다. 이런 연산들은 전처리문에서 불가능하거나 의미가 없기 때문이다. 매크로는 상수이므로 좌변값이 아니며 sizeof 연산자는 컴파일시에 평가된다. 전처리는 컴파일 이전의 단계임을 명심하도록 하자.
논리 연산자로 두 개 이상의 조건을 동시에 평가할 수 있다. C언어의 논리 연산자와 같은 &&, ||, !를 그대로 사용하면 된다.
#if (LEVEL == 8 && VER != 3)
세 개 이상의 조건도 물론 평가할 수 있다. 이때 필요하다면 조건 평가의 우선 순위 지정을 위해 괄호를 사용한다.
defined 연산자로 매크로의 존재 여부를 평가할 수 있다. #if defined(MACRO) 전처리문은 #ifdef MACRO와 완전히 동일한 문장이다. 그러나 다른 조건과 함께 매크로의 존재 여부를 평가할 때는 #ifdef를 쓸 수 없으므로 defined 연산자가 따로 제공된다.
#if (LEVEL == 8 || defined(PROFESSIONAL))
defined 연산자는 전처리문내에서만 사용되므로 일반 C코드에서는 사용할 수 없다.
#if 다음의 조건부 컴파일 블록에는 어떤 문장이든지 올 수 있다. a=b+c; 연산문이나 함수 호출문, int i; 같은 선언문은 물론이고 struct tag_A { ~ 같은 정의문도 올 수 있다. 심지어 #include, #define같은 다른 전처리문도 조건부 컴파일(정확하게 표현한다면 조건부 전처리) 대상이 될 수 있다. 그렇다면 #if안에 또 다른 #if문이 올 수 있다는 얘기가 되며 즉 #if는 중첩가능한 전처리문이다.
#if (LEVEL == 8)
LEVEL이 8일 때의 코드
#if (VER > 5)
버전이 5보다 클 때의 코드
#endif
LEVEL이 8일 때의 코드
#endif
#if안에 #ifdef가 올 수도 있고 반대도 가능하며 중첩 깊이에 제한도 없다. 조건속에 또 다른 조건이 있는 것은 자연스러운 것이므로 전처리기는 당연히 조건부 컴파일문의 중첩을 허용한다. 단, 전처리문이 중복될 경우 짝이 되는 #endif가 반드시 존재해야 한다는 것만 주의하면 된다. #endif는 조건부 컴파일 대상의 끝을 명시하는 중요한 역할을 한다. C 코드는 블록이 중첩될 때 적당히 들여쓰기를 하지만 조건부 컴파일 지시자가 중첩될 경우 들여쓰기는 하지 않는 것이 보통이다.
다음은 #if의 활용예를 보자. 어떤 문제를 해결하는데 세 가지(또는 그 이상) 방법이 있고 각 방법을 적용했을 때의 성능을 테스트해 보려고 한다. 이때 각 방법의 코드를 지웠다 넣었다 할 필요없이 다음과 같이 조건부 컴파일문으로 작성해 놓으면 METHOD 매크로만 변경하여 적용할 방법을 쉽게 선택할 수 있다. 이 코드가 다른 프로그램의 부품으로 사용되는 라이브러리이고 고객마다 선호하는 방법이 다르다면 모든 코드를 소스에 둔 채 고객의 주문대로 조건부 컴파일하기만 하면 된다.
#define METHOD 1
#if (METHOD == 1)
방법1
#elif (METHOD == 2)
방법2
#else
방법3
#endif
#if 0도 주석 대신 흔히 사용되는 구문이다. 아주 긴 소스를 잠시 주석 처리해 놓고 싶을 때는 이 부분을 #if 0 .... #endif로 감싸 버리면 항상 거짓이므로 전처리기에 의해 이 코드는 없는 것으로 취급된다. /* */ 주석은 중첩될 수 없어 긴 소스를 주석 처리할 때 불편한 반면 #if 0는 중첩 가능하기 때문에 이런 문제가 없다.


출처 : http://judoboyjin.egloos.com/viewer/4545100


'Development > C/C++' 카테고리의 다른 글

[자료형의 크기와 범위]  (0) 2014.04.22
[고급 매크로 표현식]  (0) 2014.04.22
[Static Assert]  (0) 2014.04.16
[itoa, atoi 구현]  (0) 2014.03.04
[scanf에서 공백을 포함한 문자열을 받는 방법]  (0) 2014.02.09
Posted by cyj4369
,