출처 : http://taehyo.egloos.com/4131598

태효형의 깔끔한 정리? ㅋ 

왜 커널의 많은 #defined은 do { ... } while(0)을 많이 사용할까?
다음과 같은 이유가 있다:

(from Dave Miller) 빈 구문(empty statements)은 컴파일러가 경고를 낸다.
따라서 #define FOO do { } while(0) 와 같은  구문을 사용한다.

(from Dave Miller) 지역 변수를 선언할 수 있는 기본 구역(basic block)을 마련해 준다.

(from Ben Collins) 조건문을 포함한 코드에서 복잡한 형태의 매크로를 사용할 수 있도록 해준다.
다음과 같이 몇몇 줄로 이루어진 매크로를 고려해보자:

#define FOO(x) \
        printf("arg is %s\n", x); \
        do_something_useful(x);

이제 이 코드를 다음과 같이 사용한다고 생각해보자:

if (blah == 2)
        FOO(blah);

이 코드는 다음과 같이 해석된다:

if (blah == 2)
        printf("arg is %s\n", blah);
        do_something_useful(blah);;

위에서 볼 수 있듯이 원했던 결과와는 달리 if의 범위는 prinft() 만을 포함하게 되고

do_something_useful()는 조건과 관계없이 불린다(if의 범위에 포함되지 않기 때문에).

따라서 do { ... } while(0)를 사용하여 다음과 같은 결과를 얻을 수 있다:

if (blah == 2)
        do {
                printf("arg is %s\n", blah);
                do_something_useful(blah);
        } while (0);

이것이 기대했던 결과이다.


(from Per Persson) Miller와 Collins가 지적했듯이 여러 줄의 코드를 쓰거나

지역 변수를 선언하기 위해 블록 구문(block statement)을 사용하고자 할 수도 있을 것이다.

하지만 예를 들어 아래와 같이 일반적인 블록 구문을 쓴다고 하자:

#define exch(x,y) { int tmp; tmp=x; x=y; y=tmp; }

하지만 어떤 경우에는 이 코드는 동작하지 않을 수 없다.

아래의 코드는 두개의 브랜치를 가지는 if문을 의도한 것이다:

if (x > y)
        exch(x,y);             // Branch 1
else
        do_something();     // Branch 2

하지만 이 코드는 단지 하나의 브랜치만 갖는 if문으로 해석된다.

if (x > y) {                // 단일 브랜치를 갖는 if문!!!
        int tmp;            // 블록으로 구성된
        tmp = x;           // 단 하나의 브랜치
        x = y;
        y = tmp;
}
;                             // 빈 구문
else                        // 에러!!! "parse error before else"
        do_something();

문제는 블록 바로 다음에 나오는 세미콜론(;)이다. 해결책은 블록을 do, while(0) 사이에 위치시키는 것이다.

그러면 컴파일러가 블록 구문이라고 인식하지 않는 블록의 역할을 하는 하나의 구문을 만들 수 있다.

이제 변경된 if문은 다음과 같다:

if (x > y)
        do {
                int tmp;
                tmp = x;
                x = y;
                y = tmp;
        } while(0);
else
        do_something();

(from Bart Trojanowski) gcc는 do-while-0 구문을 대체할 수 있는 구문 표현을 추가하였다.

이것은 위에 언급한 모든 이점을 갖는 동시에 좀 더 읽기 쉽다.

#define FOO(arg) ({         \
           typeof(arg) lcl;     \
           lcl = bar(arg);       \
           lcl;                      \
    })
Posted by 라판