代码之家  ›  专栏  ›  技术社区  ›  Phil Miller

C预处理器是先删除注释还是先展开宏?[复制品]

  •  54
  • Phil Miller  · 技术社区  · 15 年前

    这个问题已经有了答案:

    考虑这个(可怕的、可怕的、不好的、非常糟糕的)代码结构:

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

    显然,对于可移植的代码库来说,这是一件坏事。

    我的问题:预处理器应该如何处理这个问题?先删除注释,还是先展开宏?

    6 回复  |  直到 8 年前
        1
  •  30
  •   Reed Copsey    15 年前

    不幸的是,原版 ANSI C Specification 专门排除第4节中的任何预处理器功能(“本规范仅描述C语言)。它没有为库或预处理器做任何设置。“)。

    这个 C99 specification 不过,要明确处理这个问题。在“翻译阶段”,注释被替换为一个空格,这发生在预处理指令解析之前。(详见第6.10节)。

    VC++ 以及 GNU C Compiler 两者都遵循这种模式——如果其他编译器更老,那么它们可能不兼容,但是如果它兼容C99,那么您应该是安全的。

        2
  •  10
  •   Community noseratio    7 年前

    如上所述 this copy-n-pasted decription 在c99标准中的翻译阶段中,删除注释(它们被单个空白替换)发生在翻译阶段3中,而预处理指令在阶段4中被处理和宏扩展。

    在C90标准中(我只有硬拷贝,所以没有拷贝-粘贴),这两个阶段以相同的顺序出现,尽管翻译阶段的描述在某些细节上与C99标准略有不同-在处理预处理指令之前,注释被删除并替换为单个空白字符D和宏的展开没有什么不同。

    同样,C++标准有这2个阶段以相同的顺序发生。

    至于 // “应该处理意见,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;
    }
    

    取决于编译器是否默认为处理 trigraphs 或者没有(编译器应该这样做,但是由于三角图让几乎所有运行它们的人都感到惊讶,一些编译器默认情况下决定关闭它们),您可能会得到或者可能不会得到您想要的行为——当然,不管是什么行为。

        3
  •  5
  •   Jim Lewis    15 年前

    根据 MSDN ,在标记化技术阶段,将注释替换为单个空格, 这发生在宏展开的预处理阶段之前。

        4
  •  4
  •   Vitali    15 年前

    不要在宏中添加//注释。如果必须添加注释,请使用/**/。此外,宏中还有一个错误:

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

    这样,foo的使用总是安全的。例如:

    if (some condition)
        foo(x);
    

    无论某个表达式是否定义了foo,都不会引发编译器错误。

        5
  •  2
  •   Mike Peeler    14 年前
    #ifdef _TEST_
    #define _cerr cerr
    #else
    #define _cerr / ## / cerr
    #endif
    
    • 将在一些编译器(VC++)上工作。什么时候? _TEST_ 未定义,

      艾瑟尔…

      将替换为注释行

      //cerr…

        6
  •  1
  •   Joshua    15 年前

    我似乎记得,合规需要三个步骤:

    1. 展开宏
    2. 再带钢

    这与编译器能够直接接受.i文件有关。