## 运算符旁边带有空参数的类函数宏的标准行为?
Standard Behavior Of Function-Like Macro With Empty Argument Next To ## Operator?
举个例子:
#define FOO(x) bar x ## baz
FOO( )
根据 ANSI C 和 C99 标准,在预处理阶段之后,上述代码的预期输出是什么?
我 运行 通过 gcc -E
和 clang -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
。
举个例子:
#define FOO(x) bar x ## baz
FOO( )
根据 ANSI C 和 C99 标准,在预处理阶段之后,上述代码的预期输出是什么?
我 运行 通过 gcc -E
和 clang -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
。