전처리기 #define do{ 문장1; 문장2; 문장3 }while(0) 사용...

ktship77의 이미지

리눅스 커널분석하다 보면..

#define XX  do{ \
                         문장1; \
                         문장2; \
                    }while(0)

로 선언해 놓고,
코드 중에서

if( sss==1 )
    XX;
else
    .....

로 사용하는 경우가 많이 있습니다.
그런데.. 보니

#define XX 문장1, 문장2, 문장3

으로 해도 별 무리가 없어 보이거든요..
씨언어에 꽤 정통하신 분께 물어봐도 ... 대답은 가독성 때문이 아닐까... 라고 하던데.. 오히려 위의 것이 더 가독성이 없어 보이는데 여러분의 생각은요? 아님 아래의 것은 특정 제어문에서 동작되지 않는다든지.. 이렇건이 있는지 궁금합니다.

멋진 대답 부탁드립니다. :lol:

익명 사용자의 이미지

유명한 질문이며 kldp에서도 여러번 보았던 질문 같군요.

http://www.cinsk.org/cfaqs/html/node12.html#10.4
http://www.kernelnewbies.org/faq/#dowhile

만약 매크로 함수의 내용이 수식(expression)으로만 이루어졌다면 제안하신대로 (xxx, xxx, xxx) 와 같이 만드는 것이 나을 것입니다. 그러나 if, while과 같은 명령문(statement)이 들어가야 되는 경우에는 do while을 쓸 수밖에 없습니다.

alwaysrainy의 이미지

#define catch_exception(ce_arg) fprintf(stdout, "%sn", ce_arg) 

if ( state == -1 ) 
  catch_exception("state is null"); 


위와 같은 코드가 있을때 전처리 과정에 의해 아래와 같은 정상적인 코드로 변환되겠죠? 


if ( state == -1 ) 
  fprintf(stdout, "%sn", "state is null"); 


그런데.. 아래와 같이 define을 사용하였다고 가정합니다. 


#define catch_exception(ce_arg) 
  fprintf(stdout, "%sn", ce_arg); 
  do_something(); 

if ( state == -1 ) 
  catch_exception("state is null"); 


위의 코드는 의도하였던 바와 다르게 아래와 같이 변환됩니다. 


if ( state == -1 ) 
  fprintf(stdout, "%sn", "state is null"); 
  do_something(); 


do_something() 문은 if의 제어를 받지 않게 되는 위치에 놓이게 되겠죠? 
의도한 바에 따른 코드는 아래와 같습니다. 


if ( state == -1 ) 
{ 
  fprintf(stdout, "%sn", "state is null"); 
  do_something(); 
} 


이렇나 경우에  #defines  do { ... } while(0)을 사용하게 됩니다. 


#define catch_exception(ce_arg) 
  do { 
    fprintf(stdout, "%sn", ce_arg); 
    do_something(); 
  while ( 0 ) 


변환된 코드는 아래와 같습니다. 


if ( state == -1 ) 
{ 
  do 
  { 
    fprintf(stdout, "%sn", "state is null"); 
    do_something(); 
  } 
  while ( 0 ); 
} 

또 다른 경우를 살펴보면 아래와 같습니다.

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


익숙한 코드죠? 우선 이러한 매크로가 선언되었습니다. 


if ( x > y ) 
  exchange(x, y); 
else 
  do_something(); 


이 코드는 아래와 같이 변환되겠죠? 


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


끝에 세미콜론 하나가 무척이나 거슬림을 넘어서 맘아프게하죠? ^^ㅋ 
이 경우에도  #defines  do { ... } while(0)을 사용하게 됩니다. 


#define exchange(x, y) 
    do { int tmp; tmp = x; x = y; y = tmp; } while ( 0 ) 

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

자그마한 도움이라도 얻으셨기를 ^^;

---------------------------------------
세계는 넓고, 할일은 많다.

lifthrasiir의 이미지

ktship77 wrote:
그런데.. 보니

#define XX 문장1, 문장2, 문장3

으로 해도 별 무리가 없어 보이거든요..

물론 if/while 등의 구조를 못 쓰는 것도 그렇고, 콤마를 사용할 경우 각각의 문장이 순서대로 실행된다는 보장도 없습니다. (예를 들어서 a += b;가 a = a + b;와 동치가 아니라던지 등등등...) 단지 대부분의 컴파일러는 위 문장을 순서대로 실행하도록 컴파일할 뿐입니다.

- 토끼군

서지훈의 이미지

한 마디로...
전처리기는 문제가 없으나 인간이 실수를 하는것을 방지하기 위해 사용한다라고 보시면은 됩니다.
가독성이랑은 그렇게 관련이 없습니다.

<어떠한 역경에도 굴하지 않는 '하양 지훈'>

추신_인간의 실수는 인간의 지혜로 극복을 ...

#include <com.h> <C2H5OH.h> <woman.h>
do { if (com) hacking(); if (money) drinking(); if (women) loving(); } while (1);

doldori의 이미지

tokigun wrote:
물론 if/while 등의 구조를 못 쓰는 것도 그렇고, 콤마를 사용할 경우 각각의 문장이 순서대로 실행된다는 보장도 없습니다. (예를 들어서 a += b;가 a = a + b;와 동치가 아니라던지 등등등...) 단지 대부분의 컴파일러는 위 문장을 순서대로 실행하도록 컴파일할 뿐입니다.

그렇지 않습니다. 쉼표 연산자의 중요한 특징은 sequence point로서 동작한다는
것입니다. 따라서 쉼표 연산자로 연결된 수식들은 항상 왼쪽 피연산자부터 평가됩니다.
물론 이것이 alwaysrainy님이 지적하신 문제를 해결해주는 것은 아닙니다.
ㅡ,.ㅡ;;의 이미지

전 아래와같이사용하는데..
do while 를 왜쓰는지 모르겠군요..

define A() \
{ a(); \
b(); }

또한 do while 를 사용한메크로는 함수내부에 인자로 사용될수도 없습니다. 그러나
제가하는방식은 함수내부에 인자로도 사용될수 있는장점이 있죠..

즉 printf( "%d", (A()) ); 등도 가능합니다.


----------------------------------------------------------------------------

girneter의 이미지

alwaysrainy wrote:

끝에 세미콜론 하나가 무척이나 거슬림을 넘어서 맘아프게하죠? ^^ㅋ
이 경우에도 #defines do { ... } while(0)을 사용하게 됩니다.

#define exchange(x, y) 
    do { int tmp; tmp = x; x = y; y = tmp; } while ( 0 ) 

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

자그마한 도움이라도 얻으셨기를 ^^;

님 진짜 친절하게 자세하게 설명하셨는데
틀렸습니다.

if ( x > y )
  exchange(x, y);
else
  do_something();


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

이렇게 되겠지요.
왜 do {} while 에서는 없던 괄호가 생깁니까?

개념없는 초딩들은 좋은 말로 할때 DC나 웃대가서 놀아라. 응?

doldori의 이미지

ㅡ,.ㅡ;; wrote:
전 아래와같이사용하는데..
do while 를 왜쓰는지 모르겠군요..

define A() \
{ a(); \
b(); }

또한 do while 를 사용한메크로는 함수내부에 인자로 사용될수도 없습니다. 그러나
제가하는방식은 함수내부에 인자로도 사용될수 있는장점이 있죠..

즉 printf( "%d", (A()) ); 등도 가능합니다.


가능하지 않습니다. 함수 인자로는 식(expression)만 쓸 수 있는데 { }로 둘러싸인
것은 식이 아니라 문장(statement)입니다. 문장을 ( )로 묶는 것은 문법 오류입니다.
cppig1995의 이미지

girneter wrote:
alwaysrainy wrote:

끝에 세미콜론 하나가 무척이나 거슬림을 넘어서 맘아프게하죠? ^^ㅋ
이 경우에도 #defines do { ... } while(0)을 사용하게 됩니다.

#define exchange(x, y) 
    do { int tmp; tmp = x; x = y; y = tmp; } while ( 0 ) 

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

자그마한 도움이라도 얻으셨기를 ^^;

님 진짜 친절하게 자세하게 설명하셨는데
틀렸습니다.

if ( x > y )
  exchange(x, y);
else
  do_something();


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

이렇게 되겠지요.
왜 do {} while 에서는 없던 괄호가 생깁니까?

그래도 do~while Statement 니까 한 문장처럼 넣을 수 있잖아요.
(앗, 그게 아니라 순수한 중괄호 이야기였나?)

Real programmers /* don't */ comment their code.
If it was hard to write, it should be /* hard to */ read.

ㅡ,.ㅡ;;의 이미지

doldori wrote:
ㅡ,.ㅡ;; wrote:
전 아래와같이사용하는데..
do while 를 왜쓰는지 모르겠군요..

define A() \
{ a(); \
b(); }

또한 do while 를 사용한메크로는 함수내부에 인자로 사용될수도 없습니다. 그러나
제가하는방식은 함수내부에 인자로도 사용될수 있는장점이 있죠..

즉 printf( "%d", (A()) ); 등도 가능합니다.


가능하지 않습니다. 함수 인자로는 식(expression)만 쓸 수 있는데 { }로 둘러싸인
것은 식이 아니라 문장(statement)입니다. 문장을 ( )로 묶는 것은 문법 오류입니다.

#define A( x )          \
{                       \
    printf( "i=4\n" );  \
    i = x;              \
}


int main( void )
{
    int i;

    printf( "test[%d]\n", ( A( 4 )));
    return 0;
}


localhost test_src]$ ./a.out
i=4
test[4]

잘되는데요..
그리고 설사 안된다해도 do while 보다 낫군요..


----------------------------------------------------------------------------

cinsk의 이미지

compound statement, 즉 중괄호로 싼 statement는 expression이 아닙니다. 따라서 함수의 인자로 쓸 수 없습니다. ISO C 표준 기준.

그러나, GCC의 확장 기능으로, compound statement를 expression으로 쓸 수 있습니다. 즉 함수의 인자로 쓸 수 있습니다. 현재까지 compound statement를 expression으로 완벽하게 지원하는 것은 gcc (C compiler)이며, g++ (C++ compiler)에서는 완벽하게 지원하지 않습니다. macro를 함수처럼 정의할 때 쓸모 있으며, obstack.h에서 제공하는 routine들이 대부분 이 방식을 써서 정의됩니다.

그러나, ISO C 표준에 따르면, 이 것은 문법 오류입니다. 다만 GCC의 확장 기능으로 expression으로 평가될 수 있다는 것을 알아 두셔야 합니다.

자세한 것은

$ info gcc "c ext" stat

해 보시면 알 수 있습니다.
lifthrasiir의 이미지

cinsk 님께서 언급하신 compound statement에 대해서는 GCC 매뉴얼에 자세히 설명되어 있습니다. 이 페이지에서 예시 하나를 들면, 정수형으로 평가되는 a, b 중 큰 값을 side-effect 없이 반환하는 매크로는 다음과 같이 쓸 수 있습니다.

#define maxint(a,b) \
       ({int _a = (a), _b = (b); _a > _b ? _a : _b; })

- 토끼군

덤: 개인적으로 GCC extension 중에서는 "a ? : b"를 좋아합니다... 뭐 사실 안 쓰지만. ;)

dasomoli의 이미지

ㅡ,.ㅡ;; wrote:
doldori wrote:
ㅡ,.ㅡ;; wrote:
전 아래와같이사용하는데..
do while 를 왜쓰는지 모르겠군요..

define A() \
{ a(); \
b(); }

또한 do while 를 사용한메크로는 함수내부에 인자로 사용될수도 없습니다. 그러나
제가하는방식은 함수내부에 인자로도 사용될수 있는장점이 있죠..

즉 printf( "%d", (A()) ); 등도 가능합니다.


가능하지 않습니다. 함수 인자로는 식(expression)만 쓸 수 있는데 { }로 둘러싸인
것은 식이 아니라 문장(statement)입니다. 문장을 ( )로 묶는 것은 문법 오류입니다.

#define A( x )          \
{                       \
    printf( "i=4\n" );  \
    i = x;              \
}


int main( void )
{
    int i;

    printf( "test[%d]\n", ( A( 4 )));
    return 0;
}


localhost test_src]$ ./a.out
i=4
test[4]

잘되는데요..
그리고 설사 안된다해도 do while 보다 낫군요..

int i = 2;

if (i == 2)
  A(i);
else
  printf("Is right?");

해보세요.

아마도 이렇게 될 것 같군요

int i = 2;

if (i == 2)
{
    printf( "i=4\n" );
    i = i;
};
else  /* if 없는 else 문이 생겼군요. 이런.. */
  printf("Is right?");
      


dasomoli의 블로그(http://dasomoli.org)
dasomoli = DasomOLI = Dasom + DOLI = 다솜돌이
다솜 = 사랑하옴의 옛 고어.
Developer! ubuntu-ko! 다솜돌이 정석
ㅡ,.ㅡ;;의 이미지

위와같은경우는 {}만 있으면 잘못되는군요..
그러나 위와같은경우도 토끼군님이 예시하신방법이나
혹은 ( a(), b() ) 같이 하면됩니다.
매크로의 용도에따라 ({})처럼쓰기도 하고 {} 혹은 () 만으로
defile 하기도 하죠..
그러나 do while 은 그리 필요성을 못느끼겠더군요..


----------------------------------------------------------------------------

amister의 이미지

Quote:

위와같은경우는 {}만 있으면 잘못되는군요..
그러나 위와같은경우도 토끼군님이 예시하신방법이나
혹은 ( a(), b() ) 같이 하면됩니다.
매크로의 용도에따라 ({})처럼쓰기도 하고 {} 혹은 () 만으로
defile 하기도 하죠..
그러나 do while 은 그리 필요성을 못느끼겠더군요..

표준 신경 안쓰며 gcc 이외의 컴파일러는 사용하지 않는다면 말이죠.

alwaysrainy의 이미지

돼지군 wrote:
girneter wrote:
alwaysrainy wrote:

끝에 세미콜론 하나가 무척이나 거슬림을 넘어서 맘아프게하죠? ^^ㅋ
이 경우에도 #defines do { ... } while(0)을 사용하게 됩니다.

#define exchange(x, y) 
    do { int tmp; tmp = x; x = y; y = tmp; } while ( 0 ) 

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

자그마한 도움이라도 얻으셨기를 ^^;

님 진짜 친절하게 자세하게 설명하셨는데
틀렸습니다.

if ( x > y )
  exchange(x, y);
else
  do_something();


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

이렇게 되겠지요.
왜 do {} while 에서는 없던 괄호가 생깁니까?

그래도 do~while Statement 니까 한 문장처럼 넣을 수 있잖아요.
(앗, 그게 아니라 순수한 중괄호 이야기였나?)

ㅇ ㅏ.. 실수가 있었네요.. ^^; 중괄호는 빠져야 겠네요

---------------------------------------
세계는 넓고, 할일은 많다.

pung96의 이미지

amister wrote:
Quote:

위와같은경우는 {}만 있으면 잘못되는군요..
그러나 위와같은경우도 토끼군님이 예시하신방법이나
혹은 ( a(), b() ) 같이 하면됩니다.
매크로의 용도에따라 ({})처럼쓰기도 하고 {} 혹은 () 만으로
defile 하기도 하죠..
그러나 do while 은 그리 필요성을 못느끼겠더군요..

표준 신경 안쓰며 gcc 이외의 컴파일러는 사용하지 않는다면 말이죠.

상황에 따라 여러 매크로를 쓰거나 또 특정 컴파일러만 사용할 경우에 do while 은 오히려 거추장 스러울 수도 있겠네요.
하지만 do while가 모든 상황에서 가장 잘 적용된다는 사실은 분명하네요. 특히 코웍을 할 경우 사용자 실수를 예상하여야 한다면 상황에 따라 여러 방법을 쓸수 있는 것 보단 항상 적용되는 한가지 방법이 유리할 수 있죠.
(함수파라미터로 쓰이는 부분은 빼구요)

익명 사용자의 이미지

ㅡ,.ㅡ;; wrote:
위와같은경우는 {}만 있으면 잘못되는군요..
그러나 위와같은경우도 토끼군님이 예시하신방법이나
혹은 ( a(), b() ) 같이 하면됩니다.
매크로의 용도에따라 ({})처럼쓰기도 하고 {} 혹은 () 만으로
defile 하기도 하죠..
그러나 do while 은 그리 필요성을 못느끼겠더군요..

댓글 좀 자세히 읽어 보시길 바랍니다.

kldp에 여러번 실렸던 내용입니다.

ㅡ,.ㅡ;; 님은 모든 매크로를 어떻게 define 했는가 일일이 확인하고, 모두 외우시나요?

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.