C 预处理器是先删除注释还是先扩展宏?
考虑这个(可怕的,可怕的,不好的,非常糟糕的)代码结构:
#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);
,这对于可移植代码库来说是一件坏事。
我的问题:预处理器应该对此做什么?先删除注释,还是先扩展宏?
Consider this (horrible, terrible, no good, very bad) code structure:
#define foo(x) // commented out debugging code
// Misformatted to not obscure the point
if (a)
foo(a);
bar(a);
I've seen two compilers' preprocessors generate different results on this code:
if (a)
bar(a);
and
if (a)
;
bar(a);
Obviously, this is a bad thing for a portable code base.
My question: What is the preprocessor supposed to do with this? Elide comments first, or expand macros first?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
不幸的是,原始的 ANSI C 规范 明确排除了第 4 节中的任何预处理器功能(“本规范仅描述 C 语言,没有对库或预处理器做出任何规定。”)。
不过,C99 规范明确地处理了这个问题。在“翻译阶段”,注释被替换为单个空格,这发生在预处理指令解析之前。 (详见第 6.10 节)。
VC++ 和 GNU C 编译器 都遵循此范例 - 其他编译器如果较旧,可能不兼容,但如果它符合 C99 标准,那么您应该是安全的。
Unfortunately, the original ANSI C Specification specifically excludes any Preprocessor features in section 4 ("This specification describes only the C language. It makes no provision for either the library or the preprocessor.").
The C99 specification handles this explicity, though. The comments are replaced with a single space in the "translation phase", which happens prior to the Preprocessing directive parsing. (Section 6.10 for details).
VC++ and the GNU C Compiler both follow this paradigm - other compilers may not be compliant if they're older, but if it's C99 compliant, you should be safe.
如此副本中所述 - C99 标准中翻译阶段的 n 粘贴说明,删除注释(它们被单个空格替换)发生在翻译阶段 3,而预处理指令的处理和宏的扩展在阶段 4。
在 C90 中标准(我只有硬拷贝,所以没有复制粘贴)这两个阶段以相同的顺序发生,尽管翻译阶段的描述在某些细节上与 C99 标准略有不同 - 事实上注释是在处理预处理指令之前删除并替换为单个空白字符,并且扩展的宏没有什么不同。
同样,C++ 标准使这两个阶段以相同的顺序发生。
至于如何处理 '
//
' 注释,C99 标准是这样说的 (6.4.9/2):C++ 标准说 (2.7):
因此,您的第一个示例显然是翻译人员的错误 - 当
foo( )
宏已扩展 - 注释字符不应成为foo()
宏“内容”的一部分。但由于您面临着一个有错误的翻译器,您可能需要将宏定义更改为:
以解决该错误。
然而(我在这里偏离了主题......),由于行拼接(换行符之前的反斜杠)发生在处理注释之前,因此您可能会遇到类似这样的令人讨厌的代码:
这可能会让编写者感到惊讶它。
或者更好的是,尝试以下由喜欢框式注释的人(当然不是我!)编写的内容:
取决于您的编译器是否默认处理 三字母 或不(编译器应该这样做,但由于三字母几乎让所有遇到它们的人感到惊讶,一些编译器决定默认关闭它们),您可以或可能不会得到你想要的行为——当然,无论是什么行为。
As described in this copy-n-pasted decription of the translation phases in the C99 standard, removing comments (they are replaced by a single whitespace) occurs in translation phase 3, while preprocessing directives are handled and macros are expanded in phase 4.
In the C90 standard (which I only have in hard copy, so no copy-n-paste) these two phases occur in the same order, though the description of the translation phases is slightly different in some details from the C99 standard - the fact that comments are removed and replaced by a single whitespace character before preprocessing directives are handled and macros expanded is not different.
Again, the C++ standard has these 2 phases occur in the same order.
As far as how the '
//
' comments should be handled, the C99 standard says this (6.4.9/2):And the C++ standard says (2.7):
So your first example is clearly an error on the part of that translator - the '
;
' character after thefoo(a)
should be retained when thefoo()
macro is expanded - the comment characters should not be part of the 'contents' ofthe foo()
macro.But since you're faced with a buggy translator, you might want to change the macro definition to:
to workaround the bug.
However (and I'm drifting off topic here...), since line splicing (backslashes just before a new-line) occurs before comments are processed, you can run into something like this bit of nasty code:
Which might surprise whoever wrote it.
Or even better, try the following, written by someone (certainly not me!) who likes box-style comments:
Depending on whether your compiler defaults to processing trigraphs or not (compilers are supposed to, but since trigraphs surprise nearly everyone who runs across them, some compilers decide to turn them off by default), you may or may not get the behavior you want - whatever behavior that is, of course.
根据 MSDN,注释被替换为单个标记化阶段的空间,
这发生在扩展宏的预处理阶段之前。
According to MSDN, comments are replaced with a single space in the tokenization phase,
which happens before the preprocessing phase where macros are expanded.
切勿在宏中添加 // 注释。如果必须添加注释,请使用 /* */。另外,你的宏有一个错误:
这样, foo 总是可以安全使用。例如:
无论 foo 是否定义为某个表达式,都永远不会抛出编译器错误。
Never put // comments in your macros. If you must put comments, use /* */. In addition, you have a mistake in your macro:
This way, foo is always safe to use. For example:
will never throw a compiler error regardless of whether or not foo is defined to some expression.
适用于某些编译器 (VC++)。当
_TEST_
未定义时,_cerr ...
将被注释行替换
// cerr ...
will work on some compilers (VC++). When
_TEST_
is not defined,_cerr ...
will be replaced by the comment line
// cerr ...
我似乎记得合规需要三个步骤:
这样做的原因与编译器能够直接接受 .i 文件有关。
I seem to recall that compliance requires three steps:
The reason for this has to do with the compiler being able to accept .i files directly.