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