'C'에 해당되는 글 3건

  1. 매크로에서 do { ... } while(0) 을 사용하는 이유 (2) 2009/09/28
  2. 포인터 이해에 대한 글 2009/06/17
  3. C의 가변인자 (2) 2007/01/14
리눅스 커널 소스를 살펴보다가 헤더쪽의 매크로에서 "do { ... } while(0)" 와 같은 것이 많이 쓰인 것을 보았다.
당연히 { ... } 이 한번만 실행되고 끝나는 건데, 왜 이렇게 했을까 궁금해서 찾아보았다.

정리하자면,

1. 빈 문장(";")은 컴파일러에서 Warning 을 발생시키므로 이를 방지!
2. 지역변수를 할당할 수 있는 Basic block 을 쓸 수 있다.
3. 조건문에서 복잡한 문장을 사용할 수 있다. 예를 들면 다음과 같은 코드가 있다고 했을 때,

#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);;

뭐가 문제냐고? do_something_useful(blah); 는 조건에 관계없이 수행된다. 이는 원하는 결과가 아니다. 하지만 do { ... } while(0) 를 쓴다면 다음과 같이 해석될 것이다.

if (blah == 2)
        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

왜냐하면 다음과 같이 해석되기 때문이다.

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();

do { ... } while(0) 를 사용하면 다음과 같이 해석되어 원하는 의도대로 정확히 쓸 수 있다.

if (x > y)
        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"
RSS 리더로 나는 KLDP Geek forum 을 보곤 한다.
보다보면 재밌는 것들이 많단 말이지.. 글들에 글들을 따라가다보면 몰랐던 혹은 어렴풋이 알았던, 애매한 것들에 대해서 다시한번 생각해보게 되고 알게 된다. 언젠가 적었듯이 이게 KLDP의 묘미가 아닌가 한다.
그 중 하나로 오늘은 cinsk님의 "포인터 이해란 글에 대해."란 글을 보게 되었다. 글들에 링크되어 있는 글들의 덧글들을 보라.

세상은 넓고, 난 너무 미약하다.

Tag // C, cinsk, KLDP, 이해, 포인터

C의 가변인자

from Development 2007/01/14 02:51
IRC의 #ubuntu 에서 어제밤에 놀다보니 sakuragi 님께서 가변인자 포인터에 대해 공부하고 계신 다고 하더군요. 가변인자 포인터가 무슨 뜻일까하고 혹시 ... <- 요거요? 하고 여쭤봤더니 그게 맞는 모양이더라구요. ㅎㅎ
뭐 제가 알고있던 몇가지를 정리하자면 가변인자 포인터는 매크로 함수라는 것. 함수 호출시 인자의 포인터를 가리키게 해서 인자들을 순서대로 읽어온다는 것 정도로 알 수 있습니다. 디버거등으로 함수 호출 시의 메모리 안을 들여다보시면 쉽게 이해하실 수 있을 거예요.
저도 디버깅 함수를 만들때라던가 할 때 유용하게 써먹곤 하는데요. 뭐 vsprintf 를 사용해서 디버깅 메시지를 원하는 데로 포멧팅해서 원하는 곳에 출력한다던가 하는 정도로 사용합니다.
C99 에서는 전처리기로 처리할 수 있도록 지원하기도 하는데(이게 정확한 표현이 아닐수도 있습니다; 뭐라고 써야할지 정확히 생각나진 않네요.) 제가 사용했던 ARM Development Suite 1.2 에 들어가 있는 ARMCC 에서 다음과 같이 작성해서 쓴 적이 있습니다. 코드 일부를 옮기자면..

// 디버그 함수
#ifndef        _DEBUG_
#define        DBG_OUT(...)    ;
#else
#define        DBG_OUT(...)    at91_usart_printf(&USART0_DESC, __VA_ARGS__)
#endif

이런 식으로 선언해서 써주었었죠. at91_usart_printf 함수는 제가 구현해서 썼던 함수인데 함수 내부에서 vsprintf 를 사용해서 가변인자 형식을 받아 printf처럼 포멧팅해서 내부시리얼로 전송하는 함수였습니다. 디버깅 메시지를 시리얼로 표시하고 싶을 땐 그냥 DBG_OUT("값: %d\r\n", value); 형식으로 써주면 됐었죠. 저는 매우 게으르므로 디버깅시마다 같은 글자를 매번 타이핑하기가 귀찮았거든요. 줄여쓰면 좋잖아요? ㅎㅎㅎ

처음 C 배울 때 printf 함수 정의의 ... 으로 처리된 저건 어떻게 쓰는 걸까 하고 생각했던 기억이 떠오르기도 하네요. 궁금하신 분들은 서적이나 MSDN 등을 한번 뒤져보시는 것도 좋습니다.
제가 그렇게 배웠거든요 :D(하수의 이야기;)

Tag // C, 가변인자