programing

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

sourcetip 2021. 1. 15. 20:25
반응형

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


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

다음 (끔찍하고 끔찍하고 좋지 않음, 매우 나쁨) 코드 구조를 고려하십시오.

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

// 문자는 다음 개행 문자로 끝나는 주석을 시작합니다.

따라서 첫 번째 예는 분명히 해당 번역기 부분의 오류입니다. 매크로가 확장 될 때 ;뒤에 ' '문자 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;
}

Depending on whether your compiler defaults to processing trigraphs or not (compilers are supposed to, but since trigraphs surprise nearly everyone who runs across them, some compilers decide to turn them off by default), you may or may not get the behavior you want - whatever behavior that is, of course.


According to MSDN, comments are replaced with a single space in the tokenization phase, which happens before the preprocessing phase where macros are expanded.


Never put // comments in your macros. If you must put comments, use /* */. In addition, you have a mistake in your macro:

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

This way, foo is always safe to use. For example:

if (some condition)
    foo(x);

will never throw a compiler error regardless of whether or not foo is defined to some expression.


#ifdef _TEST_
#define _cerr cerr
#else
#define _cerr / ## / cerr
#endif
  • will work on some compilers (VC++). When _TEST_ is not defined,

    _cerr ...

    will be replaced by the comment line

    // cerr ...


I seem to recall that compliance requires three steps:

  1. strip
  2. expand macros
  3. strip again

The reason for this has to do with the compiler being able to accept .i files directly.

ReferenceURL : https://stackoverflow.com/questions/1510869/does-the-c-preprocessor-strip-comments-or-expand-macros-first

반응형