类函数宏的扩展创建一个单独的标记

Expansion of function-like macro creates a separate token

我刚刚发现 gcc 似乎将类函数宏的扩展结果视为单独的标记。这是一个显示 gcc 行为的简单示例:

#define f() foo
void f()_bar(void);
void f()bar(void);
void f()-bar(void);

当我执行 gcc -E -P test.c(运行 只是预处理器)时,我得到以下输出:

void foo _bar(void);
void foo bar(void);
void foo-bar(void);

看起来,在前两个定义中,gcc 在扩展宏之后插入 space 以确保它是一个单独的标记。这真的是这里发生的事情吗?

这是任何标准强制执行的吗(我找不到关于该主题的文档)?

我想使 _bar 成为同一个标记的一部分。有什么办法吗?我可以使用令牌连接运算符 ## 但它需要几个级别的宏(因为在实际代码中 f() 更复杂)。我想知道是否有一个简单(并且可能更易读)的解决方案。

我能想到的唯一方法(如果你不能使用标记连接运算符##)是使用传统的(预标准)C 预处理:

gcc -E -P -traditional-cpp test.c

输出:

void foo_bar(void);
void foobar(void);
void foo-bar(void);

More info

看起来,在前两个定义中,gcc 在扩展宏之后插入 space 以确保它是一个单独的标记。这真的是这里发生的事情吗?

是的。

这是任何标准强制要求的吗(我找不到关于该主题的文档)?

是的,尽管允许实现插入多个白色space来分隔标记。

f()_bar

经过词法分析后,这里有 4 个标记(在这个阶段它们实际上是预处理器标记,但我们称它们为标记):f()_bar.

类函数宏替换语义(如 C11,6.10.3 中所定义)必须将 3 个标记 f() 替换为一个新的 foo。不允许对其他令牌进行操作并更改最后一个 _bar 令牌。为此,实现必须插入至少一个 whitespace 以保留 _bar 标记。否则结果将是 foo_bar 这是一个单一的标记。

gcc 预处理器有点 documents 在这里:

Once the input file is broken into tokens, the token boundaries never change, except when the ‘##’ preprocessing operator is used to paste tokens together. See Concatenation. For example,

#define foo() bar
foo()baz
     ==> bar baz
not
     ==> barbaz

在另一种情况下,如 f()-bar,有 5 个标记:f()-bar . (- 是 C 中的标点符号,而 _bar 中的 _ 只是标识符标记的一个字符)。该实现不必在此处插入标记分隔符(如白色space),因为在宏替换后 -bar 仍被视为来自 C 语法的两个单独的标记。

gcc 预处理器 (cpp) 不会在这里插入 whitespace 只是因为它没有必要。在 cpp documentation 中,在标记间距上写着(在不同的问题上):

However, we would like to keep space insertion to a minimum, both for aesthetic reasons and because it causes problems for people who still try to abuse the preprocessor for things like Fortran source and Makefiles.

我没有在这个答案中解决您的问题,但我认为您必须使用明确指定的运算符来连接标记:## 标记粘贴运算符。