C 전처리기가 주석을 제거하거나 매크로를 먼저 확장합니까?[복제하다]

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

문제

이 질문에는 이미 답변이 있습니다.

다음(끔찍함, 끔찍함, 좋지 않음, 매우 나쁨) 코드 구조를 고려해보세요.

#define foo(x) // commented out debugging code

// Misformatted to not obscure the point
if (a)
foo(a);
bar(a);

두 컴파일러의 전처리기가 이 코드에서 서로 다른 결과를 생성하는 것을 보았습니다.

if (a)
bar(a);

그리고

if (a)
;
bar(a);

분명히 이것은 이식 가능한 코드 기반에는 나쁜 것입니다.

내 질문:전처리기는 이것으로 무엇을 해야 합니까?댓글을 먼저 삭제하시겠습니까, 아니면 매크로를 먼저 확장하시겠습니까?

도움이 되었습니까?

해결책

불행히도 원본 ANSI C 사양 구체적으로 섹션 4의 사전 처리기 기능을 제외합니다 ( "이 사양은 C 언어 만 설명합니다. 라이브러리 또는 사전 처리기에 대해 제공하지 않습니다.").

그만큼 C99 사양 그러나이 설명을 처리합니다. 주석은 "번역 단계"의 단일 공간으로 대체되며, 이는 전처리 지침 구문 분석 전에 발생합니다. (자세한 내용은 섹션 6.10).

VC ++ 그리고 GNU C 컴파일러 둘 다이 패러다임을 따릅니다. 다른 컴파일러는 나이가 많은 경우 준수하지 않을 수 있지만 C99를 준수하는 경우 안전해야합니다.

다른 팁

에 설명된 대로 이 복사하여 붙여넣은 설명은 C99 표준의 번역 단계 중 주석 제거(단일 공백으로 대체됨)는 번역 단계 3에서 발생하고 전처리 지시어는 처리되고 매크로는 단계 4에서 확장됩니다.

C90 표준(하드 카피로만 있으므로 복사하여 붙여넣기가 불가능함)에서는 이 두 단계가 동일한 순서로 발생하지만 번역 단계에 대한 설명은 일부 세부 사항에서 C99 표준과 약간 다릅니다. 전처리 지시어가 처리되고 매크로가 확장되기 전에 주석이 제거되고 단일 공백 ​​문자로 대체된다는 점은 다르지 않습니다.

다시 말하지만, C++ 표준에서는 이 두 단계가 동일한 순서로 발생합니다.

어디까지나 '//' 주석은 ​​처리되어야 하며 C99 표준에서는 다음과 같이 말합니다(6.4.9/2).

캐릭터 상수, 문자열 문자 또는 주석을 제외하고, 문자 // //는 다음 새로운 라인 문자까지까지 모든 멀티 바이트 문자를 포함하는 주석을 소개합니다.

그리고 C++ 표준에는 (2.7)이 나와 있습니다.

문자 // 다음 Newline 캐릭터와 종료되는 주석을 시작합니다.

따라서 귀하의 첫 번째 예는 분명히 해당 번역자의 오류입니다.;' 문자 뒤에 foo(a) 때 보관해야합니다. foo() 매크로가 확장되었습니다. 주석 문자는 매크로의 '내용'에 포함되어서는 안 됩니다. the foo() 매크로.

그러나 버그가 있는 변환기에 직면했으므로 매크로 정의를 다음과 같이 변경하는 것이 좋습니다.

#define foo(x) /* junk */

버그를 해결하려면.

그러나 (여기서는 주제에서 벗어났습니다...) 주석이 처리되기 전에 줄 연결(새 줄 바로 앞의 백슬래시)이 발생하므로 다음과 같은 불쾌한 코드가 발생할 수 있습니다.

#define evil( x) printf( "hello "); // hi there, \
                 printf( "%s\n", x); // you!



int main( int argc, char** argv)
{
    evil( "bastard");

    return 0;
}

누가 쓴 것인지 놀랄 수도 있습니다.

또는 더 나은 방법은 상자 스타일 댓글을 좋아하는 사람(확실히 저는 아닙니다!)이 작성한 다음을 시도해 보는 것입니다.

int main( int argc, char** argv)
{
                            //----------------/
    printf( "hello ");      // Hey, what the??/
    printf( "%s\n", "you"); // heck??         /
                            //----------------/
    return 0;
}

컴파일러가 기본적으로 처리를 수행하는지 여부에 따라 다름 삼중문자 또는 그렇지 않습니다(컴파일러는 그렇게 해야 하지만 trigraph는 이를 실행하는 거의 모든 사람을 놀라게 하기 때문에 일부 컴파일러는 기본적으로 이를 끄기로 결정합니다). 물론 어떤 동작이든 원하는 동작을 얻을 수도 있고 얻지 못할 수도 있습니다.

에 따르면 MSDN, 주석은 토큰 화 단계에서 단일 공간으로 대체되며, 이는 매크로가 확장되는 전처리 단계 전에 발생합니다.

당신의 매크로에 // 댓글을 넣지 마십시오. 주석을 넣어야하는 경우 / * * /를 사용하십시오. 또한 매크로에 실수가 있습니다.

#define foo(x) do { } while(0) /* junk */

이런 식으로 Foo는 항상 사용하기에 안전합니다. 예를 들어:

if (some condition)
    foo(x);

FOO가 일부 표현식에 정의되어 있는지 여부에 관계없이 컴파일러 오류를 던지지 않습니다.

#ifdef _TEST_
#define _cerr cerr
#else
#define _cerr / ## / cerr
#endif
  • 일부 컴파일러 (VC ++)에서 작동합니다. 언제 _TEST_ 정의되지 않으며

    _cerr ...

    주석 줄로 대체됩니다

    // Cerr ...

준수는 세 단계가 필요하다는 것을 기억하는 것 같습니다.

  1. 조각
  2. 매크로를 확장하십시오
  3. 다시 벗겨냅니다

그 이유는 컴파일러가 .i 파일을 직접 수락 할 수있는 것과 관련이 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top