考虑这个(可怕的,可怕的,没有好的,非常糟糕的)代码结构:
#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评论,还是先扩展宏?
遗憾的是,最初的ANSI C规范明确排除了第4节中的任何预处理器功能("此规范仅描述了C语言.它没有为库或预处理器提供任何规定.").
但是,C99规范处理了这种明显性.在"转换阶段"中,注释将替换为单个空格,该空间在预处理指令解析之前发生.(详见第6.10节).
VC++和GNU C编译器都遵循这个范例 - 如果它们更老,其他编译器可能不兼容,但如果它符合C99,那么你应该是安全的.
如在C99标准中的翻译阶段的复制粘贴描述中所述,在翻译阶段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; }
根据你的编译器是否默认处理三字符(编译器应该是这样,但由于三元组几乎让每个运行它们的人感到惊讶,一些编译器决定默认关闭它们),你可能会也可能不会得到你想要的行为 -当然,无论是什么行为.
根据MSDN,在标记化阶段用单个空格替换注释,这在宏扩展的预处理阶段之前发生.