## 运算符旁边带有空参数的类函数宏的标准行为?

Standard Behavior Of Function-Like Macro With Empty Argument Next To ## Operator?

举个例子:

#define FOO(x) bar x ## baz
FOO( )

根据 ANSI C 和 C99 标准,在预处理阶段之后,上述代码的预期输出是什么?

我 运行 通过 gcc -Eclang -E,两者都产生了等同于以下内容的输出:

bar baz


还有,如果上面的预处理输出认为是符合标准的,那这个呢?

#define FOO(x) x ## baz
FOO( )

经过上述修改后,GCC 和 clang 仍会产生等同于以下内容的输出,而不会发出任何警告或错误(即使使用 -Wall -Werror 标志):

baz


我怀疑上述输出不符合标准的原因是 ANSI C 9899:1990 标准规定:

6.8.3.3 The ## operator

A ## preprocessing token shall not occur at the beginning or at the end of a replacement list for either form of macro definition.

If, in the replacement list, a parameter is immediately preceded or followed by a ## preprocessing token, the parameter is replaced by the corresponding argument's preprocessing token sequence.

好的,所以 技术上 ## 运算符不在上述两个示例中的替换列表的开头,但是 x 确实扩展到...单个 space?没有什么? ...因此 ## 运算符(至少,据我所知)正在将 space pp-token(或什么都没有)连接到 baz


此外,标准规定:

For both object-like and function-like macro invocations, before the replacement list is reexamined for more macro names to replace, each instance of a ## preprocessing token in the replacement list (not from an argument) is deleted and the preceding preprocessing token is concatenated with the following preprocessing token.

那么,考虑到我在上面给出的第一个例子,为什么正确的输出不应该是这个?

barbaz


更新

作为旁注,我确实设法让 GCC 在预处理代码时为上面给出的第一个示例发出错误,如下所示:

gcc -ansi -pedantic -Wall -Werror -E ex1.c -o -

输出(有错误)是:

foo.c:2:6: error: invoking macro FOO argument 1: empty macro arguments are undefined in ISO C90 [-Werror=pedantic]
 FOO( )
      ^
# 1 "foo.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "foo.c"

bar baz
cc1: all warnings being treated as errors

但是,该错误与 ## 运算符的行为无关,这就是这个问题的全部内容。尽管如此,它仍然很有趣。

另外值得注意的是,在使用相同的标志预处理文件时,clang 没有发出任何错误。

如果你有这个:

#define FOO(x) bar x ## baz                
FOO();                                  

你得到 bar baz。所以 FOO 调用中的 space 不相关。

如果你有这个:

#define FOO(x) bar ## x ## baz                 
FOO();                                  

你得到 barbaz 因为你要求将所有这些粘贴在一起。

但是你写:

#define FOO(x) bar x ## baz                 
FOO();                                  

你得到 bar baz 因为你要求 bar 以及 x 和 baz 的粘贴。这类似于 #define FOO(x) bar,x ## baz 给你 bar,baz.

Okay, so technically the ## operator is NOT at the beginning of the replacement list in both of the above two examples,

这里的"technically"这个词是多余的。您的两个宏的替换列表在 ## 之前都有 x,因此该规则不适用。

but x does expand to... a single space? nothing?

6.4 列出了被认为是预处理标记的内容:header-name、标识符、pp-number、character-constant、string-literal、标点符号和 "each non-whitespace character that cannot be one of the above"。请注意此列表中明显缺少的内容......空白本身。

所以你在 FOO 的论点中的空格并不重要;您的论点是一系列 0 个预处理标记(即上面列表中的 0 个)。这意味着这适用:

6.10.3.3 p2:

however, if an argument consists of no preprocessing tokens, the parameter is replaced by a placemarker preprocessing token instead.

下一段 3 适用于此处的结果:

concatenation of a placemarker with a non-placemarker preprocessing token results in the non-placemarker preprocessing token.

因此,将 placemarker 粘贴到 baz 会生成 baz