C 预处理器是先删除注释还是先扩展宏?

发布于 2024-08-06 01:01:40 字数 371 浏览 5 评论 0原文

考虑这个(可怕的,可怕的,不好的,非常糟糕的)代码结构:

#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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

空气里的味道 2024-08-13 01:01:40

不幸的是,原始的 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.

活雷疯 2024-08-13 01:01:40

此副本中所述 - C99 标准中翻译阶段的 n 粘贴说明,删除注释(它们被单个空格替换)发生在翻译阶段 3,而预处理指令的处理和宏的扩展在阶段 4。

在 C90 中标准(我只有硬拷贝,所以没有复制粘贴)这两个阶段以相同的顺序发生,尽管翻译阶段的描述在某些细节上与 C99 标准略有不同 - 事实上注释是在处理预处理指令之前删除并替换为单个空白字符,并且扩展的宏没有什么不同。

同样,C++ 标准使这两个阶段以相同的顺序发生。

至于如何处理 '//' 注释,C99 标准是这样说的 (6.4.9/2):

除了字符常量、字符串文字或注释之外,字符 //
引入一个注释,其中包括所有多字节字符,但不包括
下一个换行符。

C++ 标准说 (2.7):

字符 // 开始注释,以下一个换行符结束
性格。

因此,您的第一个示例显然是翻译人员的错误 - 当 foo( ) 宏已扩展 - 注释字符不应成为 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;
}

取决于您的编译器是否默认处理 三字母 或不(编译器应该这样做,但由于三字母几乎让所有遇到它们的人感到惊讶,一些编译器决定默认关闭它们),您可以或可能不会得到你想要的行为——当然,无论是什么行为。

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):

Except within a character constant, a string literal, or a comment, the characters //
introduce a comment that includes all multibyte characters up to, but not including, the
next new-line character.

And the C++ standard says (2.7):

The characters // start a comment, which terminates with the next newline
character.

So your first example is clearly an error on the part of that translator - the ';' character after the foo(a) should be retained when the foo() macro is expanded - the comment characters should not be part of the 'contents' of the foo() macro.

But since you're faced with a buggy translator, you might want to change the macro definition to:

#define foo(x) /* junk */

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:

#define evil( x) printf( "hello "); // hi there, \
                 printf( "%s\n", x); // you!



int main( int argc, char** argv)
{
    evil( "bastard");

    return 0;
}

Which might surprise whoever wrote it.

Or even better, try the following, written by someone (certainly not me!) who likes box-style comments:

int main( int argc, char** argv)
{
                            //----------------/
    printf( "hello ");      // Hey, what the??/
    printf( "%s\n", "you"); // heck??         /
                            //----------------/
    return 0;
}

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.

狂之美人 2024-08-13 01:01:40

根据 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.

画骨成沙 2024-08-13 01:01:40

切勿在宏中添加 // 注释。如果必须添加注释,请使用 /* */。另外,你的宏有一个错误:

#define foo(x) do { } while(0) /* junk */

这样, foo 总是可以安全使用。例如:

if (some condition)
    foo(x);

无论 foo 是否定义为某个表达式,都永远不会抛出编译器错误。

Never put // comments in your macros. If you must put comments, use /* */. In addition, you have a mistake in your macro:

#define foo(x) do { } while(0) /* junk */

This way, foo is always safe to use. For example:

if (some condition)
    foo(x);

will never throw a compiler error regardless of whether or not foo is defined to some expression.

攒一口袋星星 2024-08-13 01:01:40
#ifdef _TEST_
#define _cerr cerr
#else
#define _cerr / ## / cerr
#endif
  • 适用于某些编译器 (VC++)。当_TEST_未定义时,

    _cerr ...

    将被注释行替换

    // cerr ...

#ifdef _TEST_
#define _cerr cerr
#else
#define _cerr / ## / cerr
#endif
  • will work on some compilers (VC++). When _TEST_ is not defined,

    _cerr ...

    will be replaced by the comment line

    // cerr ...

偏爱自由 2024-08-13 01:01:40

我似乎记得合规需要三个步骤:

  1. 展开宏
  2. 再次剥离

这样做的原因与编译器能够直接接受 .i 文件有关。

I seem to recall that compliance requires three steps:

  1. strip
  2. expand macros
  3. strip again

The reason for this has to do with the compiler being able to accept .i files directly.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文