리눅스 커널 소스를 살펴보다가 헤더쪽의 매크로에서 “do { … } while(0)” 와 같은 것이 많이 쓰인 것을 보았다.
당연히 { … } 이 한번만 실행되고 끝나는 건데, 왜 이렇게 했을까 궁금해서 찾아보았다.
정리하자면,
1. 빈 문장(“;”)은 컴파일러에서 Warning 을 발생시키므로 이를 방지!
2. 지역변수를 할당할 수 있는 Basic block 을 쓸 수 있다.
3. 조건문에서 복잡한 문장을 사용할 수 있다. 예를 들면 다음과 같은 코드가 있다고 했을 때,
#define FOO(x) \
printf(“arg is %s\n”, x); \
do_something_useful(x);
printf(“arg is %s\n”, x); \
do_something_useful(x);
다음과 같이 이 매크로를 사용한다면,
if (blah == 2)
FOO(blah);
FOO(blah);
이렇게 해석되어서 쓰여진다.
if (blah == 2)
printf(“arg is %s\n”, blah);
do_something_useful(blah);;
printf(“arg is %s\n”, blah);
do_something_useful(blah);;
뭐가 문제냐고? do_something_useful(blah); 는 조건에 관계없이 수행된다. 이는 원하는 결과가 아니다. 하지만 do { … } while(0) 를 쓴다면 다음과 같이 해석될 것이다.
if (blah == 2)
do {
printf(“arg is %s\n”, blah);
do_something_useful(blah);
} while (0);
do {
printf(“arg is %s\n”, blah);
do_something_useful(blah);
} while (0);
정확히 원하는 결과를 얻을 수 있다.
4. 3번과 같은 경우에 다음과 같이 사용할 수도 있지 않냐고 생각할 수 있다.
#define exch(x,y) { int tmp; tmp=x; x=y; y=tmp; }
그러나 다음과 같은 경우에는 원하는데로 동작하지 않는다.
if (x > y)
exch(x,y); // Branch 1
else
do_something(); // Branch 2
exch(x,y); // Branch 1
else
do_something(); // Branch 2
왜냐하면 다음과 같이 해석되기 때문이다.
if (x > y) { // Single-branch if-statement!!!
int tmp; // The one and only branch consists
tmp = x; // of the block.
x = y;
y = tmp;
}
; // empty statement
else // ERROR!!! “parse error before else”
do_something();
int tmp; // The one and only branch consists
tmp = x; // of the block.
x = y;
y = tmp;
}
; // empty statement
else // ERROR!!! “parse error before else”
do_something();
do { … } while(0) 를 사용하면 다음과 같이 해석되어 원하는 의도대로 정확히 쓸 수 있다.
if (x > y)
do {
int tmp;
tmp = x;
x = y;
y = tmp;
} while(0);
else
do_something();
do {
int tmp;
tmp = x;
x = y;
y = tmp;
} while(0);
else
do_something();
5. gcc에서는 Statements and Declarations in Expressions 확장을 사용할 수 있다. 이는 위에서 본 do-while-0 Block 대신 쓸 수 있다.
참고 : KLDP의 “의미없는 do while 문“
kernelnewbies.org의 “FAQ/DoWhile0“