C预处理器是否首先删除注释或扩展macros?
考虑这个(可怕的,糟糕的,不好的,非常糟糕的)代码结构:
#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);
显然,这对于便携式代码库是一件坏事。
我的问题:预处理器应该怎么做? 请先评论一下,或先扩展macros?
不幸的是,最初的ANSI C规范明确地排除了第4节中的任何预处理器特性(“本规范只描述了C语言,它没有规定库或预处理器”)。
不过, C99规范处理了这个问题。 在“预处理”指令parsing之前,“翻译阶段”中的注释被replace为单个空格。 (细节6.10)。
VC ++和GNU C编译器都遵循这种模式 – 其他编译器可能不符合,如果它们是旧的,但如果它符合C99,则应该是安全的。
正如在C99标准中对翻译阶段进行复制粘贴的描述中所描述的,在翻译阶段3中删除注释(它们被单个空白符替代),同时处理预处理指令并且在阶段4中扩展macros。
在C90标准(我只有硬拷贝,所以没有复制粘贴)这两个阶段以相同的顺序发生,尽pipe翻译阶段的描述与C99标准的某些细节略有不同 – 事实在处理预处理指令和扩展macros之前,删除注释并将其replace为单个空白字符。
再次,C ++标准有这两个阶段以相同的顺序发生。
至于应该如何处理“ //
”的注释,C99标准说这(6.4.9 / 2):
除了字符常量,string字符或注释之外,字符//会引入一个注释,该注释包含所有多字节字符,但不包括下一个换行符。
而C ++标准说(2.7):
字符//开始注释,以下一个换行符结尾。
所以你的第一个例子显然是译者的错误 – 当foo()
macros被展开时,应保留foo(a)
之后的字符 – 注释字符不应该成为the foo()
macros的“内容”的一部分。
但是,由于您面对的是一个错误的翻译器,您可能需要将macros定义更改为:
#define foo(x) /* junk */
解决这个错误。
然而(我在这里漂stream的话题…),因为在处理注释之前发生了行拼接(在一个换行符之前的反斜杠),所以你可以碰到类似这样的恶意代码:
#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 (编译器应该这样做,但是由于trigraphs几乎让所有运行它们的人感到惊讶,一些编译器决定默认closures它们),你可能会也可能不会得到你想要的行为 -当然,无论什么行为。
根据MSDN ,注释在标记化阶段被replace为单个空间,这在扩展macros的预处理阶段之前发生。
永远不要把/ /评论在你的macros。 如果您必须发表评论,请使用/ * * /。 另外,你的macros中有一个错误:
#define foo(x) do { } while(0) /* junk */
这样,foo总是安全的使用。 例如:
if (some condition) foo(x);
无论foo是否定义为某个expression式,都不会抛出编译器错误。
#ifdef _TEST_ #define _cerr cerr #else #define _cerr / ## / cerr #endif
-
将在一些编译器(VC ++)上工作。 当
_TEST_
未定义时,_cerr …
将被注释行取代
// …
我似乎记得,遵守要求三个步骤:
- 跳闸
- 展开macros
- 再次剥离
原因是编译器能够直接接受.i文件。