考虑这个(可怕的,可怕的,没有好的,非常糟糕的)代码结构:
#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);
显然,对于可移植的代码库来说这是件坏事。
我的问题:预处理器应该用这个做什么?首先是Elide注释,还是先扩展宏?
答案 0 :(得分:30)
不幸的是,原始ANSI C Specification明确排除了第4节中的任何预处理器功能(“此规范仅描述了C语言。它没有为库或预处理器提供任何条件。”)。
C99 specification处理这种明显。在“转换阶段”中,注释将替换为单个空格,该空间在预处理指令解析之前发生。 (详见第6.10节)。
VC++和GNU C Compiler都遵循这个范例 - 如果它们年龄较大,其他编译器可能不合规,但如果它符合C99,那么您应该是安全的。
答案 1 :(得分:10)
如C99标准中的this copy-n-pasted decription所述,删除注释(它们被单个空格替换)发生在转换阶段3,同时处理预处理指令并在阶段4中扩展宏。
在C90标准中(我只有硬拷贝,所以没有copy-n-paste)这两个阶段以相同的顺序出现,尽管转换阶段的描述在某些细节上与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;
}
取决于你的编译器是否默认处理trigraphs(编译器应该是这样,但由于三元组几乎让所有运行它们的人感到惊讶,一些编译器决定默认关闭它们),你可能会或者可能没有得到你想要的行为 - 无论当然是什么行为。
答案 2 :(得分:5)
根据MSDN,在标记化阶段,注释被替换为单个空格, 这是在扩展宏的预处理阶段之前发生的。
答案 3 :(得分:4)
永远不要在您的宏中添加//注释。如果必须发表评论,请使用/ * * /。另外,你的宏中有一个错误:
#define foo(x) do { } while(0) /* junk */
这样,foo总是可以安全使用。例如:
if (some condition)
foo(x);
无论是否将foo定义为某个表达式,都不会抛出编译器错误。
答案 4 :(得分:2)
#ifdef _TEST_
#define _cerr cerr
#else
#define _cerr / ## / cerr
#endif
将适用于某些编译器(VC ++)。如果未定义_TEST_
,
_cerr ...
将被评论专栏
取代// cerr ...
答案 5 :(得分:1)
我似乎记得合规需要三个步骤:
原因与编译器能够直接接受.i文件有关。