C预处理器是先去掉注释还是先展开宏?

C预处理器是先去掉注释还是先展开宏?,c,comments,c-preprocessor,c99,C,Comments,C Preprocessor,C99,考虑以下代码结构(可怕、可怕、不好、非常糟糕): #define foo(x) // commented out debugging code // Misformatted to not obscure the point if (a) foo(a); bar(a); 我看到两个编译器的预处理器在此代码上生成不同的结果: if (a) bar(a); 及 显然,对于可移植代码库来说,这是一件坏事 我的问题:预处理器应该如何处理这个问题?先删除注释,或先展开宏?根据,注释在标记化阶段被替换

考虑以下代码结构(可怕、可怕、不好、非常糟糕):

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

// Misformatted to not obscure the point
if (a)
foo(a);
bar(a);
我看到两个编译器的预处理器在此代码上生成不同的结果:

if (a)
bar(a);

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

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

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

不幸的是,原始版本在第4节中明确排除了任何预处理器功能(“本规范仅描述C语言。它既不提供库也不提供预处理器。”)

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

这两种编译器都遵循这一模式——如果其他编译器较旧,则可能不兼容,但如果它兼容C99,则应该是安全的。

如C99标准中的翻译阶段中所述,删除注释(它们被单个空格替换)发生在翻译阶段3,在第4阶段,处理预处理指令并展开宏

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

再次,C++标准有2个阶段,它们的顺序相同。 关于如何处理“

/
”注释,C99标准规定如下(6.4.9/2):

除了在字符常量、字符串文字或注释中,字符// 引入一条注释,该注释包含所有多字节字符,最多包括,但不包括 下一个新行字符

和C++标准说(2.7):

字符//开始一条注释,注释以下一个换行符结束 性格

因此,您的第一个示例显然是该翻译器的一个错误,“
foo()
宏时,应保留
foo(a)
后的code>'字符-注释字符不应是
foo()
宏的“内容”的一部分

但是,由于您面临一个有缺陷的转换器,您可能希望将宏定义更改为:

#define foo(x) /* junk */
来解决这个bug

然而(我在这里偏离了主题…),因为行拼接(在新行之前的反斜杠)发生在注释处理之前,所以您可能会遇到类似下面这种讨厌的代码:

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

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

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

#define foo(x) do { } while(0) /* junk */
这样,foo使用起来总是安全的。例如:

if (some condition)
    foo(x);

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

我似乎记得遵从性需要三个步骤:

  • 展开宏
  • 再脱光衣服

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

    #ifdef _TEST_
    #define _cerr cerr
    #else
    #define _cerr / ## / cerr
    #endif
    
    • 将在一些编译器(VC++)上工作。如果未定义
      \u测试

      _瑟尔

      将替换为注释行

      //瑟尔


    好问题-让我努力跟踪实际信息:)仅供参考,使用“#define foo(x)##”执行更安全的空白宏。。。。(或者是####?:/)顺便问一下,您使用的编译器在第一个示例中起作用?我很确定它会破坏很多代码-即使在#define中只使用/**/注释可能是明智的,但我的印象是我看到了大量使用的“//”注释。可能是预处理器不理解
    /
    注释,而编译器理解吗?请记住,最初,C应该只理解代码> > **/< /COD>注释,而<>代码//< /COD>是C++扩展。我认为C只在C99上获得了
    /
    。(我的历史记录正确吗?)。事实上,无论您使用什么编译器,我都很好奇它是如何处理
    /*
    对不起,但是您链接到的不是ANSI C规范;实际规范描述了第2.1.1.2节中的翻译阶段;不久前,我发布了一篇关于这些阶段的概述:是的,不确定。我总是使用(大部分)C99兼容的编译器。不过,OP似乎使用的是C99编译器,因为C89中不存在//as注释。我遇到的许多C编译器都支持C++/C99'//'注释,即使它们不支持C99中的任何其他内容。@Novelocrat:正如我所说的,如果您使用的是C99,您是安全的。但从技术上讲,几乎没有完全兼容的C99编译器(例如,MS和GNU都不是100%兼容的)。或者将注释从#define行移开。问题的关键是,
    junk
    是实际代码,在不用于调试时被注释掉。这个答案很好,并且包含真正帮助我的真理宝石-在宏中使用
    /***/
    -风格的注释,我很安全。
    垃圾
    是有时作为宏主体的代码。我的前任们没有这么小心。你能澄清一下w吗
    #ifdef _TEST_
    #define _cerr cerr
    #else
    #define _cerr / ## / cerr
    #endif