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

时间:2018-11-20 06:17:40

标签: c c-preprocessor preprocessor

采用以下示例:

#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 ##运算符

     

对于任何一种形式的宏定义, ## 预处理令牌都不应出现在替换列表的开头或结尾。

     

如果在替换列表中,参数紧跟在 ## 预处理标记之前或之后,则该参数将替换为相应参数的预处理标记序列。

好的,所以在以上两个示例中,从技术上来说 ## 都不在替换列表的开头,但是x确实可以扩展到...一个空格?没有? ...所以 ## 运算符(至少,据我所知)正在将一个空间pp令牌(或什么都没有)连接到baz


此外,标准规定:

  

对于类对象和函数类宏调用,在重新检查替换列表以查找更多要替换的宏名称之前,替换列表中每个 ## 预处理令牌的实例(不是来自参数),然后将前面的预处理令牌与后面的预处理令牌连接起来。

因此,考虑到我上面给出的第一个示例,为什么不应该将正确的输出显示为这样?

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不会发出错误。

2 个答案:

答案 0 :(得分:5)

好的,因此从技术上讲,在以上两个示例中,##运算符都不在替换列表的开头,

这里的“技术上”一词是多余的。两个宏的替换列表在x之前都有##,因此该规则不适用。

但是x确实可以扩展到...一个空格吗?什么都没有?

6.4列出了被认为是预处理令牌的内容:报头名称,标识符,pp编号,字符常量,字符串文字,标点符号和“每个不能为上述字符之一的非空白字符”。请注意,此列表中明显没有什么...空格本身。

因此,您对FOO的论点中的空格并不重要;您的参数是一个由0个预处理标记组成的序列(即,上面列表中的0个对象)。这意味着这适用:

6.10.3.3 p2:

但是,如果参数不包含预处理令牌,则该参数将替换为地标预处理令牌。

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

将地标与非地标预处理令牌连接起来会产生非地标预处理令牌。

因此,粘贴到baz上的 placemarker 会生成baz

答案 1 :(得分:0)

如果您有这个:

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

您得到bar baz。因此,FOO调用中的空间不相关。

如果您有这个:

#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