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 标准规定参数 a
和 b
在连接发生之前不是宏展开的。 (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"
在此示例中,a
和 b
在调用 cc
之前扩展为 fu
和 bar
。所以现在 puts(concat(fu,BAR));
将打印“fubar”。
Here ## produces an error about a stray ## in the code.
因为你只能在宏里面使用#
和##
,就这么简单。 extern int fu##BAR ();
不是宏。
看起来用##连接有时有效,有时无效。
这是一个不可靠的功能,尽管它对于某些用途来说显然是非常必要的。
是否有一套明确的##使用规则?
一个例子是:
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 标准规定参数 a
和 b
在连接发生之前不是宏展开的。 (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"
在此示例中,a
和 b
在调用 cc
之前扩展为 fu
和 bar
。所以现在 puts(concat(fu,BAR));
将打印“fubar”。
Here ## produces an error about a stray ## in the code.
因为你只能在宏里面使用#
和##
,就这么简单。 extern int fu##BAR ();
不是宏。