728x90

do { ... } while(0) 을 사용하는 이유

원문 : https://kernelnewbies.org/FAQ/DoWhile0

리눅스 커널 등의 소스 코드를 보게 되면, "do { ... } while(0)"과 같은 것을 많이 씁니다.
당연히 중괄호 안의 내용이 한 번만 실행되고 끝나는 프로그램인데, 왜 이렇게 만들었을까요?

다음과 같은 이유가 있습니다.
    ▶ Miller : 빈 구문(empty statement)는 컴파일러가 경고를 냅니다.
    ▶ 
Miller : 지역 변수를 선언할 수 있는 구역을 만들어줍니다.
    ▶ 
Collins : 조건문을 포함한 코드에서 복잡한 형태의 매크로를 사용할 수 있도록 해줍니다.

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

위와 같은 매크로를 조건문과 함께 사용하게 된다면,

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

이렇게 사용될 것이고, 이는 곧 다음과 같이 해석됩니다.

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

여기서 문제되는 것은, do_something_useful(blah); 가 조건에 관계없이 수행된다는 점입니다. 이는 원하는 결과가 아닙니다. 하지만, do { ... } while(0) 를 쓴다면 다음과 같이 해석될 것입니다.

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

정확히 원하는 결과를 얻을 수 있게 됩니다.

Miller와 Collins가 지적했듯이, 여러 줄의 코드를 작성하거나
지역 변수를 사용하기 위해 블록 구문을 사용할 수도 있을 것입니다.

하지만, 다음과 같이 일반적인 블록 구문을 쓴다 가정했을 때

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

하지만 특정한 경우에 위의 코드는 작동하지 않습니다.

아래 코드는 두 개의 조건을 가지는 if 문을 의도한 것입니다.

if (x > y)
    exch(x,y);            // 조건 1
else
    do_something();       // 조건 2
cs

하지만, 이 코드는 단지 하나의 조건만 갖는 if문으로 해석됩니다.

if (x > y) {        // if문
    int tmp;        // 블록으로 구성된
    tmp = x;        // 하나의 조건문
    x = y;
    y = tmp;
}
;                   // 빈 구문
else                // 에러 "parse error before else"
    do_something();
cs

문제는 if문 블록 바로 다음에 나오는 세미콜론(;)입니다.
이 문제의 해결책은 do, while(0) 사이에 위치시키는 것인데,
그러면 컴파일러가 블록 구문이라고 인식하지 않는 블록의 역할을 하는 하나의 구문을 만들 수 있습니다.

변경된 if문은 다음과 같습니다.

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

gcc에서는 이 do-while-0 구문을 대체할 수 있는 구문 표현을 추가했습니다. 
이 표현은 위에 언급한 모든 이점을 갖는 동시에 좀 더 읽기 쉽습니다.

gcc에서 추가된 구문

#define FOO(arg) ({        \
       typeof(arg) lcl;    \
       lcl = bar(arg);     \
       lcl;                \
})
cs


728x90
728x90