C预处理器##在什么情况下起作用,什么情况下不起作用?

In what situations does C preprocessor ## work and not work?

看起来用##连接有时有效,有时无效。

这是一个不可靠的功能,尽管它对于某些用途来说显然是非常必要的。

是否有一套明确的##使用规则?

一个例子是:

file1.h
#define concat(a,b) a##b
#define BAR bar
extern int concat(fu,BAR) ();

此处 concat 生成 fuBAR 而不是 fubar。

一个例子是:

file2.h
#define BAR bar
extern int fu##BAR ();

此处## 会在代码中产生关于杂散## 的错误。

## 遵守的规则与您期望的规则不同。这并不意味着它“不起作用”或“是一个不可靠的功能”,它仅意味着您必须以不同于您想象的方式使用它。

写的时候

#define concat(a,b) a##b

C 标准规定参数 ab 在连接发生之前不是宏展开的。 (N1570 §6.10.3.1。)这与您不将 ### 应用于宏参数时的行为有 故意 差异。

你可以通过双重扩展获得你想要的行为:

#define concat(a,b) concat_(a,b)
#define concat_(a,b) a##b

根据这个定义,concat 的参数是 宏在替换到宏体之前展开的,因为它们不被用作 [=13= 的操作数].然后每个参数都成为 concat_ 的参数,并且不会再次扩展,但这很好,因为扩展已经完成。

当你写作时

#define BAR bar
extern int fu##BAR ();

C 标准说 ## 运算符根本不被预处理器识别(因此传递到翻译阶段 7,它是一个有效的标记,不被任何语法规则接受,因此会导致语法错误),因为它不是宏定义的一部分。 (N1570 §6.10.3.3p2,3 — 仅暗示;它说当 ## 出现在正在扩展的宏的替换列表中时 被识别 。本节没有t 说它在任何其他时间都被识别,并且标准的其他部分没有给出 ## 在任何其他上下文中的含义。)

It seems that sometimes concatenation with ## does work and sometimes it does not work.

这就像在说“有时与 * 的乘法有效,有时却无效”,然后举例说明

int six = 5;
int product = six * 2; // results in 10 instead of 12

void f(int a, int b, int a * b);  // rejected by the compiler

token-pasting 运算符的规范在 the C17 language specification 的 6.10.3.3 中介绍,您的第一个线索应该是这是 6.10.3 的一个小节,它描述了宏替换。不仅该部分的位置而且其显式文本都清楚地表明标记连接(仅)发生在宏替换的上下文中:

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.

(C17 6.10.3.3/3)

##没有其他特殊处理的规范,所以这就是它的全部范围。

此外,

After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded.

(C17 6.10.3.1/1;已强调)

因此,function-like 宏参数是 ##(或 #)运算符的操作数,它们本身不是 macro-expanded,尽管串联结果可能是当结果为 re-scanned.

时扩展为宏

Is there a clear set of rules for using ##?

C 标准 6.10.3.1:

After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded.

基本上 ## 连接发生在标记扩展之前,因为 ## 的结果可能会产生一个新的预处理器标记。如果我们有这个:

#define concat(a,b) a##b
#define BAR bar
#define fuBAR "hello world"

然后 puts(concat(fu,BAR)); 将打印 "hello world".

为了解决这个问题,您需要一个辅助宏来扩展预处理器标记 ,然后 将它们传递给宏 ##(或 # ) 位于:

#define cc(a,b) a##b
#define concat(a,b) cc(a,b)

#define BAR bar
#define fuBAR "hello world"
#define fubar "fubar"

在此示例中,ab 在调用 cc 之前扩展为 fubar。所以现在 puts(concat(fu,BAR)); 将打印“fubar”。


Here ## produces an error about a stray ## in the code.

因为你只能在宏里面使用###,就这么简单。 extern int fu##BAR (); 不是宏。