考虑这个(太可怕了,太可怕了,没有好,非常糟糕的)代码结构:
#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);
显然,这是一个可移植的代码基础一件坏事。
我的问题:什么是应该用这个做预处理? 第一评论的Elid,或首先展开宏?
不幸的是,原来的ANSI C规范明确排除在第4任何预处理器的功能(“本说明书仅描述了C语言,它使没有规定任何库或预处理器”)。
该C99规范处理此明确地,虽然。 该意见被替换为“翻译阶段”一个单一的空间,其中预处理指令解析之前发生。 (第6.10节的详细信息)。
VC ++和GNU C编译器都遵循此模式-如果他们年长其他编译器可能不兼容的,但如果它的C99兼容的,你应该是安全的。
正如在描述这个副本正粘贴能解密在C99标准翻译阶段,删除注释(它们由单个空格置换)发生在翻译阶段3,而预处理指令的处理和宏在相位4中展开。
在C90标准(我只在硬拷贝,所以没有抄正膏)这两个阶段发生在相同的顺序,虽然转换阶段的描述是C99标准的一些细节略有不同 - 事实上该评论被删除,预处理指令之前,由单一的空白字符替换的处理和扩大宏是没有什么不同。
同样,C ++标准具有发生在相同的顺序这2个相。
至于如何“ //
”意见应该如何处理,C99标准说,这(6.4.9 / 2):
除了一个字符内是恒定的,字符串文字或注释,字符//引入一个注释,包括所有多字节字符最多,但不包括下一个新行字符。
和C ++标准说(2.7):
字符//开始一个注释,与下一个换行符终止。
所以,你的第一个例子显然是对翻译的部分错误-在“ ;
'后字符foo(a)
应当保留当foo()
宏展开-注释字符不应该的一部分‘的内容’ the 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;
}
根据是否你的编译器的默认处理三合与否(编译器都应该,但由于三合惊喜几乎每个人都谁碰到他们运行,一些编译器决定在默认情况下将其关闭),你可能会或可能不会得到你想要的行为-什么行为是的,当然。
根据MSDN ,注释替换为标记化阶段的单一空间,在预处理阶段,其中宏展开之前恰好。
永远不要把//评论在您的宏。 如果你必须把注释,使用/ * * /。 此外,你在你的宏错误:
#define foo(x) do { } while(0) /* junk */
这样一来,foo是永远放心使用。 例如:
if (some condition)
foo(x);
不会抛出一个编译错误不管FOO是否被定义为一些表达。
#ifdef _TEST_
#define _cerr cerr
#else
#define _cerr / ## / cerr
#endif