을 사용하는 이유는 분명히 무의미지는 경우-다른 문서 매크로가?

StackOverflow https://stackoverflow.com/questions/154136

  •  03-07-2019
  •  | 
  •  

문제

에서 많은 C/C++매크로 나를 보고 코드의 매크로 감싸서 보이는 것이 무의미 do while 루프입니다.여기에는 예입니다.

#define FOO(X) do { f(X); g(X); } while (0)
#define FOO(X) if (1) { f(X); g(X); } else

나는 볼 수없는 무엇인 do while 이 하고 있습니다.왜 다만 쓰이지 않고 그것은?

#define FOO(X) f(X); g(X)
도움이 되었습니까?

해결책

do ... whileif ... else 가 할 수 있도록 세미콜론 한 후에 당신의 매크로를 항상 같은 것을 의미한다.당신 뭔가를했다처럼 당신의 두 번째 매크로입니다.

#define BAR(X) f(x); g(x)

지금 하는 경우 사용 BAR(X);if ... else 문의 몸이 if 감지 않았습에 대괄호,당신을 놀라움이다.

if (corge)
  BAR(corge);
else
  gralt();

위의 코드는 것으로 확장

if (corge)
  f(corge); g(corge);
else
  gralt();

는 구문으로 잘못으로 다른 사람은 더 이상과 관련된 경우.그것은 도움이 되지 않을 감싸는 것이 중괄호 안에 매크로하기 때문에,세미콜론 후에는 교정기는 구문으로 잘못되었습니다.

if (corge)
  {f(corge); g(corge);};
else
  gralt();

방법은 두 가지의 고정하는 문제입니다.첫 번째는 쉼표를 사용하여 순서 제표에서 매크로를 강탈없이 그것은 그것의 능력 행동처럼 표현이다.

#define BAR(X) f(X), g(X)

위의 버전의 바 BAR 확장 위의 코드는 무엇으로 다음과 같이는 구문으로 정확하다.

if (corge)
  f(corge), g(corge);
else
  gralt();

이 작동하지 않는 경우에는 대신 f(X) 당신은 더 복잡는 코드 본문에 갈 필요가 있는 자신의 블록은,예를 들어 예를 들어 지역 변수를 선언.에서 가장 일반적인 케이스 솔루션은 같은 것을 사용하는 do ... while 하는 원인이 매크로를 하나의 문이는 세미콜론합니다.

#define BAR(X) do { \
  int i = f(X); \
  if (i > 4) g(i); \
} while (0)

당신을 사용할 필요가 없 do ... while, 할 수 있 요리가 뭔가를 가진 if ... else 뿐만 아니라우 if ... else 확장의 내부는 if ... else 그것은"매달려 다른 사람"수 있는 기존 매달려 다른 사람도 문제를 어렵게 찾아보기에는 다음 코드입니다.

if (corge)
  if (1) { f(corge); g(corge); } else;
else
  gralt();

포인트가 사용하는 세미콜론 컨텍스트에서 매달려 세미콜론은 잘못된 것입니다.물론 그것이 수 있는(아마도 해야한다)주장했다 이 시점에서 수 있을 것이라고 선언 BAR 으로 실제 기능이 아닌 매크로입니다.

요약하면, do ... while 이 있는 작업의 단점을 보완 C 전처리기.그 때는 C 스타일 가이드에 당신을 말할을 해 C 전처리기는,이것은 종류의 것들에 대한 걱정합니다.

다른 팁

매크로는 사전 프로세서가 진정한 코드로 넣을 텍스트 복사/붙여 넣은 텍스트입니다. 매크로의 저자는 교체가 유효한 코드를 생성하기를 희망합니다.

성공하기위한 세 가지 좋은 "팁"이 있습니다.

매크로가 진정한 코드처럼 행동하도록 도와줍니다

일반 코드는 일반적으로 세미콜론으로 끝납니다. 사용자 뷰 코드가 필요하지 않으면 ...

doSomething(1) ;
DO_SOMETHING_ELSE(2)  // <== Hey? What's this?
doSomethingElseAgain(3) ;

즉, 사용자는 반 콜론이없는 경우 컴파일러가 오류를 생성 할 것으로 예상합니다.

그러나 진짜 진짜 이유는 때때로 매크로의 저자가 매크로를 진정한 기능으로 바꿔야 할 것입니다. 그래서 매크로는해야합니다 진짜 하나처럼 행동합니다.

그래서 우리는 세미콜론이 필요한 매크로가 있어야합니다.

유효한 코드를 생성합니다

JFM3의 답변에 표시된 바와 같이, 때로는 매크로에는 둘 이상의 명령이 포함되어 있습니다. 그리고 매크로가 IF 문 안에 사용되면 문제가됩니다.

if(bIsOk)
   MY_MACRO(42) ;

이 매크로는 다음과 같이 확장 될 수 있습니다.

#define MY_MACRO(x) f(x) ; g(x)

if(bIsOk)
   f(42) ; g(42) ; // was MY_MACRO(42) ;

그만큼 g 함수는 값에 관계없이 실행됩니다 bIsOk.

이것은 매크로에 범위를 추가해야한다는 것을 의미합니다.

#define MY_MACRO(x) { f(x) ; g(x) ; }

if(bIsOk)
   { f(42) ; g(42) ; } ; // was MY_MACRO(42) ;

유효한 코드 2를 생성합니다

매크로가 다음과 같은 경우

#define MY_MACRO(x) int i = x + 1 ; f(i) ;

다음 코드에서 또 다른 문제가 발생할 수 있습니다.

void doSomething()
{
    int i = 25 ;
    MY_MACRO(32) ;
}

다음과 같이 확장되기 때문입니다.

void doSomething()
{
    int i = 25 ;
    int i = 32 + 1 ; f(i) ; ; // was MY_MACRO(32) ;
}

물론이 코드는 컴파일되지 않습니다. 다시, 솔루션은 범위를 사용하고 있습니다.

#define MY_MACRO(x) { int i = x + 1 ; f(i) ; }

void doSomething()
{
    int i = 25 ;
    { int i = 32 + 1 ; f(i) ; } ; // was MY_MACRO(32) ;
}

코드가 다시 올바르게 작동합니다.

세미콜론 + 스코프 효과를 결합 하시겠습니까?

이 효과를 생성하는 하나의 C/C ++ 관용구가 있습니다.

do
{
    // code
}
while(false) ;

do/while은 범위를 생성하여 매크로의 코드를 캡슐화 할 수 있으며 결국 세미콜론이 필요하므로 코드가 필요한 코드로 확장됩니다.

보너스?

C ++ 컴파일러는 조건이 컴파일 시간에 알려져 있기 때문에 C ++ 컴파일러는 DO/While 루프를 최적화합니다. 이것은 매크로가 다음과 같은 것을 의미합니다.

#define MY_MACRO(x)                                  \
do                                                   \
{                                                    \
    const int i = x + 1 ;                            \
    f(i) ; g(i) ;                                    \
}                                                    \
while(false)

void doSomething(bool bIsOk)
{
   int i = 25 ;

   if(bIsOk)
      MY_MACRO(42) ;

   // Etc.
}

다음과 같이 올바르게 확장됩니다

void doSomething(bool bIsOk)
{
   int i = 25 ;

   if(bIsOk)
      do
      {
         const int i = 42 + 1 ; // was MY_MACRO(42) ;
         f(i) ; g(i) ;
      }
      while(false) ;

   // Etc.
}

그런 다음 컴파일되고 AS를 최적화합니다

void doSomething(bool bIsOk)
{
   int i = 25 ;

   if(bIsOk)
   {
      f(43) ; g(43) ;
   }

   // Etc.
}

@jfm3- 질문에 대한 좋은 대답이 있습니다. 또한 매크로 관용구가 간단한 'if'진술로 의도하지 않은 동작 (오류가 없기 때문에)을 방지 할 수 있다고 덧붙일 수도 있습니다.

#define FOO(x)  f(x); g(x)

if (test) FOO( baz);

확장 :

if (test) f(baz); g(baz);

이는 구문 적으로 정확하므로 컴파일러 오류가 없지만 G ()가 항상 호출되는 의도하지 않은 결과를 초래할 수 있습니다.

위의 답변은 이러한 구성의 의미를 설명하지만 언급되지 않은 두 가지 사이에는 큰 차이가 있습니다. 사실, 선호 할 이유가 있습니다. do ... while ~로 if ... else 건설하다.

의 문제 if ... else 구성은 그렇지 않다는 것입니다 당신은 세미콜론을 넣습니다. 이 코드와 마찬가지로 :

FOO(1)
printf("abc");

우리는 세미콜론을 제외했지만 (실수로) 코드는

if (1) { f(X); g(X); } else
printf("abc");

조용히 컴파일됩니다 (일부 컴파일러는 도달 할 수없는 코드에 대한 경고를 발행 할 수 있지만). 하지만 printf 진술은 결코 실행되지 않습니다.

do ... while 구조물은 그러한 문제가 없습니다. while(0) 세미콜론입니다.

컴파일러는 최적화 할 것으로 예상됩니다 do { ... } while(false); 루프, 해당 구조물이 필요하지 않은 다른 솔루션이 있습니다. 해결책은 쉼표 연산자를 사용하는 것입니다.

#define FOO(X) (f(X),g(X))

또는 더욱 이국적으로 :

#define FOO(X) g((f(X),(X)))

이것은 별도의 지침으로 잘 작동하지만 변수가 구성되어 사용되는 경우에는 작동하지 않습니다. #define :

#define FOO(X) (int s=5,f((X)+s),g((X)+s))

이것으로 하나는 do/while construct를 사용해야합니다.

Jens Gustedt 's P99 사전 처리기 라이브러리 (그렇습니다. 그런 것이 존재한다는 사실도 내 마음을 날려 버렸습니다!) if(1) { ... } else 다음을 정의하여 작지만 중요한 방식으로 구성하십시오.

#define P99_NOP ((void)0)
#define P99_PREFER(...) if (1) { __VA_ARGS__ } else
#define P99_BLOCK(...) P99_PREFER(__VA_ARGS__) P99_NOP

이것에 대한 이론적 근거는 그것과 달리입니다 do { ... } while(0) 건설하다, break 그리고 continue 주어진 블록 내부에서 여전히 작동하지만 ((void)0) 매크로 호출 후 세미콜론이 생략되면 구문 오류를 만듭니다. 그렇지 않으면 다음 블록을 건너 뜁니다. (여기서는 실제로 "매달려있는"문제가 없습니다. else 가장 가까운 곳에 묶습니다 if, 그것은 매크로에있는 것입니다.)

C 프리 프로세서와 더 안전하게 수행 할 수있는 일에 관심이 있으시면 해당 라이브러리를 확인하십시오.

몇 가지 이유로 첫 번째 답변에 대해 언급 할 수 없습니다 ...

여러분 중 일부는 로컬 변수가있는 매크로를 보여 주었지만 매크로에서 이름을 사용할 수는 없다고 언급 한 사람은 없습니다! 그것은 언젠가 사용자를 물릴 것입니다! 왜요? 입력 인수는 매크로 템플릿으로 대체되기 때문입니다. 그리고 당신의 거시 예에서 당신은 아마도 가장 일반적으로 사용되는 다양한 이름을 사용합니다. .

예를 들어 다음 매크로

#define FOO(X) do { int i; for (i = 0; i < (X); ++i) do_something(i); } while (0)

다음 기능에서 사용됩니다

void some_func(void) {
    int i;
    for (i = 0; i < 10; ++i)
        FOO(i);
}

매크로는 some_func의 시작 부분에 선언 된 의도 된 변수 i를 사용하지 않지만, 매크로의 루프 중에 선언 된 로컬 변수.

따라서 매크로에서 공통 변수 이름을 사용하지 마십시오!

설명

do {} while (0) 그리고 if (1) {} else 매크로가 1 개의 명령으로 만 확장되었는지 확인해야합니다. 그렇지 않으면:

if (something)
  FOO(X); 

확장됩니다 :

if (something)
  f(X); g(X); 

그리고 g(X) 외부에서 실행됩니다 if 제어 진술. 이것은 사용될 때 피합니다 do {} while (0) 그리고 if (1) {} else.


더 나은 대안

GNU와 함께 진술 표현 (표준 C의 일부가 아님), 당신은 do {} while (0) 그리고 if (1) {} else 이것을 해결하기 위해 간단히 사용하여 ({}):

#define FOO(X) ({f(X); g(X);})

그리고이 구문은 반환 값과 호환됩니다 ( do {} while (0) 그렇지 않음)에서와 같이 :

return FOO("X");

나는 그것이 언급되었다고 생각하지 않으므로 이것을 고려하십시오

while(i<100)
  FOO(i++);

번역됩니다

while(i<100)
  do { f(i++); g(i++); } while (0)

방법에 주목하십시오 i++ 매크로에 의해 두 번 평가됩니다. 이것은 흥미로운 오류로 이어질 수 있습니다.

내가 찾는 이릭 매우 도움이 되는 상황에서 당신은 순차적으로 처리 특정한 값입니다.의 각 수준에서 처리하는 경우,오류 또는 잘못된 조건이 발생하면,당신은 피할 수 있는 더 이상 처리고 일찍.예:

#define CALL_AND_RETURN(x)  if ( x() == false) break;
do {
     CALL_AND_RETURN(process_first);
     CALL_AND_RETURN(process_second);
     CALL_AND_RETURN(process_third);
     //(simply add other calls here)
} while (0);
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top