jin*_*123 5 c preprocessor c-preprocessor
请看以下示例:
#define FOO(x) bar x ## baz
FOO( )
Run Code Online (Sandbox Code Playgroud)
在预处理阶段之后,根据ANSI C和C99标准,上述代码的预期输出是多少?
我跑的上面通过gcc -E
和clang -E
,两者产生的输出等效于以下语句:
bar baz
Run Code Online (Sandbox Code Playgroud)
另外,如果上述预处理输出被认为符合标准,那么这个呢?
#define FOO(x) x ## baz
FOO( )
Run Code Online (Sandbox Code Playgroud)
通过上面的修改,GCC和clang仍然产生相当于以下的输出,而不会发出任何警告或错误(即使有-Wall -Werror
标志):
baz
Run Code Online (Sandbox Code Playgroud)
我怀疑上述输出不符合标准的原因是ANSI C 9899:1990标准规定:
6.8.3.3 ##运算符
一个##预处理标记不得在开头或用于宏定义的任一形式的替代列表的末尾出现.
如果在替换列表中,参数紧跟在##预处理标记之前或之后,则该参数将由相应参数的预处理标记序列替换.
好的,从技术上来说,##运算符不是上述两个示例中的替换列表的开头,但x
确实扩展到......单个空格?没有?...所以##运算符(至少,据我所知)是将空格pp-token(或无)连接到baz
.
此外,标准规定:
对于这两种对象类和函数宏调用,进行复核更多宏名称来替换替换列表之前,一个的每个实例##在替换列表(而不是从一个参数)预处理标记被删除,并且前述预处理标记与以下预处理标记连接.
所以,考虑到我上面给出的第一个例子,为什么不应该正确输出呢?
barbaz
Run Code Online (Sandbox Code Playgroud)
作为旁注,我确实设法让GCC在预处理代码时给出了上面给出的第一个例子的错误,如下所示:
gcc -ansi -pedantic -Wall -Werror -E ex1.c -o -
Run Code Online (Sandbox Code Playgroud)
输出(有错误)是:
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
Run Code Online (Sandbox Code Playgroud)
但是,错误与##运算符的行为无关,这就是这个问题的全部内容.尽管如此,它仍然很有趣.
另外值得注意的是,在使用相同的标志预处理文件时,clang不会发出错误.
好的,从技术上来说,##运算符不在以上两个示例中的替换列表的开头,
这里"技术上"这个词是多余的.两个宏的替换列表都在x
之前##
,因此该规则不适用.
但x确实扩展到......一个空间?没有?
6.4列出了被认为是预处理标记的内容:header-name,identifier,pp-number,character-constant,string-literal,punctuator,以及"不能是上述之一的每个非空白字符".请注意此列表中明显缺少的内容......空白内容.
所以你的论证中的空白FOO
并不重要; 你的参数是0个预处理标记的序列(即上面列表中的0个).这意味着这适用:
6.10.3.3 p2:
但是,如果参数不包含预处理标记,则参数将替换为地标标记预处理标记.
下一段3适用于此处的结果:
地标标记与非标记预处理标记的串联导致非标记预处理标记.
所以,地标贴在baz
产品上baz
.