가끔식 코드를 보면 아래와 같이 매크로에 do {..} while(0) 문을 쓰는 것을 볼 수 있다.
#define TEST() \
do { \
\
\
} while (0)
이렇게 코드를 만들어주는 이유는 아래와 같다.
1. 지역 변수를 선언할 수 있는 scope를 만들어준다.
#define TEST() \
do { \
int i = 0; \
} while (0)
하지만 굳이 do {...} while(0) 문이 아니고 그냥 중괄호를 사용해도 지역변수를 선언할 수 있다.
#define TEST() \
{ \
int i = 0; \
} while (0)
그렇다면, "do {...} while (0) 문 대신 그냥 중괄호를 사용하여 지역변수를 할당할 수 있도록 하면되는 것 아닌가?" 라는 의문이 든다.
하지만, do {...} while (0) 문을 사용해야하는 이유는 아래를 보면 끄덕여진다.
2. 조건문이 있는 코드에서 복잡한 형태의 매크로를 사용할 수 있도록 해준다.
우선 매크로가 코드 내에서 어떻게 처리되는지 알아야한다.
우리가 만든 코드는 전처리 -> 컴파일 -> 링킹 -> 실행파일 생성 과정을 거친다. 컴파일 단계는 사용자가 작성한 코드를 CPU가 실행할 수 있는 인스트럭션으로 치환해주는 단계이다.
가령, int 형 변수 i에 1을 넣어주는 코드를 컴파일 하면 아래와 같이 cpu가 실행할 수 있는 인스트럭션으로 치환된다.
int main () {
int i = 1;
}
mov dword ptr [ebp-4], 1 // 1이라는 상수 값을 dword ptr [ebp-4] 메모리 주소로 옮긴다.
컴파일 과정이 코드를 인스트럭션으로 치환하는 과정이라면, 전처리는 컴파일 과정이 이루어질 수 있도록 전처리 자리에 코드를 삽입하는 과정이다. 코드를 삽입해줘야 CPU가 처리할 수 있는 인스트럭션으로 치환가능하기 때문이다.
#define TEST() \
printf ("TEST1 \n"); \
printf ("TEST2 \n");
int main (void) {
if (0)
TEST();
return 0;
}
위의 코드가 전처리 과정을 거치면 main 함수의 TEST() 부분이 TEST 매크로의 구현부로 변환된다.
#define TEST() \
printf ("TEST1 \n"); \
printf ("TEST2 \n");
int main (void) {
if (0)
printf("TEST1 \n");
printf("TEST2 \n");;
return 0;
}
전처리 과정이 정상적으로 진행되었지만, 개발자가 의도한 코드가 아니다.
"if 절의 조건이 거짓이면, TEST 매크로를 호출하여라"가 개발자가 의도한 코드이지만, 의도와는 다르게, if문이 참이든 거짓이든 TEST2는 무조건 출력이된다.
TEST2
그리고 TEST()가 치환되면서 TEST(); 에 있던 ;이 남아 세미콜론이 2개가 되었다. 이 상황은 어떤 OS에서는 Warning만 내고 동작할 수 있으나, 어떤 OS에서는 빌드 에러가 날 수 있다.
printf("TEST2 \n");; //어떤 컴파일러를 쓰느냐에 따라 허용될 수도 있다.
우선, TEST 매크로를 중괄호로 감싸면 어떤 문제가 발생하는지 확인해보자.
중괄호로 TEST 매크로를 감싼다음, 전처리 과정을 거치면 아래와 같이 코드가 만들어진다.
#define TEST() { \
printf ("TEST1 \n"); \
printf ("TEST2 \n"); \
}
int main (void) {
if (0) {
printf("TEST1 \n");
printf("TEST2 \n");
};
return 0;
}
위의 코드를 실행하면, 개발자가 의도한대로 아무것도 출력이 되지 않는다.
문제가 해결된 듯 보이지만, 만약에 if문이 아니라 if ~ else문을 사용했다면 어떤 일이 발생할까?
#define TEST() { \
printf ("TEST1 \n"); \
printf ("TEST2 \n"); \
}
int main (void) {
if (0)
TEST();
else
printf("Here is true\n");
return 0;
}
위의 코드를 전처리를 하면, 아래와 같이 변환 되면서 컴파일 에러가 발생한다. if 절과 else 절 사이에 세미콜론이 있기 때문이다.
#define TEST() { \
printf ("TEST1 \n"); \
printf ("TEST2 \n"); \
}
int main (void) {
if (0) {
printf("TEST1 \n");
printf("TEST2 \n");
}; //세미콜론 때문에 컴파일 에러 발생
else
printf("Here is true\n");
return 0;
}
error: 'else' without a previous 'if'
이 문제를 해결하기 위해, do {...} while (0) 문을 사용한다.
do ~ while문은 사실 마지막 부분에 아래와 같이 세미콜론을 붙여줘야한다.
do {
...
} while (조건);
do ~ while문의 끝에 세미콜론이 있어야하는 것을 이용하면 세미콜론 문제를 해결할 수 있다.
우선, 매크로에서는 세미콜론을 제거하여 do ~ while문을 작성한다. 그리고 전처리가 진행되었을 때 TEST();의 세미콜론이 do ~ while문의 세미콜론으로 적용되면 된다.
#define TEST()
do { \
printf ("TEST1 \n"); \
printf ("TEST2 \n"); \
} while (0)
int main (void) {
if (0)
do {
printf("TEST1 \n");
printf("TEST2 \n");
} while (0); //전처리 후 TEST();의 세미콜론이 do ~ while의 세미콜론이 된다.
else
printf("Here is true\n");
return 0;
}
[정리]
매크로에 do ~ while (0)을 사용하면,
1. 매크로 안에 지역변수를 사용할 수 있게 된다. (scope 생성)
2. if절과 매크로를 사용했을 때, 개발자가 의도한대로 동작하게 한다.
3. if ~ else절을 사용했을 때 발생할 수 있는 세미콜론 문제가 없어진다.