2015-09-26 105 views
-1

最近我看到一些像这样的代码:了解宏扩展

​​

我测试的代码,调用这样的:

JOIN(Foo, 0); 
JOIN_(Foo, 1); 
JOIN__(Foo, 2); 

JOIN(Foo, JOIN(A,B)); 
JOIN_(Foo, JOIN(A,B)); 
JOIN__(Foo, JOIN(A,B)); 

宏扩展为以下符号:

Foo0 
Foo1 
Foo2 
FooAB 
FooAB 
FooJOIN 

我得到了目的,它解决了不同的论点。在最后一种情况下,调用JOIN的任何变体显然都不相同。但是这些宏如何扩展?为什么参数的行为有所不同?

编辑:Here's文件

+0

我认为,最后一个宏'JOIN __(Foo,JOIN(A,B));'应该扩展为'FooJOIN(A,B)'而不只是'FooJOIN',请澄清。 –

+0

这是可能的。我得到'警告:函数'FooJOIN'的隐式声明在C99'中是无效的,所以我真的不知道它得到了哪些参数。但是你的意思是有道理的 –

回答

1

EDIT

3.9.6参数预扫

宏参数是完全宏扩展它们是 代入宏体之前,除非它们被字符串化或与其他令牌粘贴 。替换之后,将再次扫描包括替换参数在内的整个宏体 ,宏将扩展为 。结果是参数被扫描两次以在其中扩展 宏调用。

调用其他宏的字符串化或连接的宏。如果 参数被字符串化或级联,则不会发生预扫描。 如果您想要展开宏,然后将其展开或连接其扩展,可以通过使一个宏调用另一个执行串化或串联的宏 来实现。

举例来说,如果你有

#define AFTERX(x) X_ ## x 
#define XAFTERX(x) AFTERX(x) 
#define TABLESIZE 1024 
#define BUFSIZE TABLESIZE 

然后AFTERX(BUFSIZE)扩展到X_BUFSIZE,并XAFTERX(BUFSIZE)扩展到X_1024。 (不X_TABLESIZE。预扫描总是做了完整的扩展。)

CASE1

#define JOIN__(lhs, rhs) lhs##rhs =>因为它有一个标记粘贴操作者,它会连接这些宏参数那些不完全宏观膨胀前取代。 - >这是一种糟糕的扩展方式,我们不知道的第一个地方,它将传递给它的参数是什么,它不会等待它的扩展,而只是将它连接起来。

因此,当您拨打JOIN__(Foo, JOIN(A,B));时,它将不允许JOIN(A,B)展开,并将它连接到FOOJOIN(A,B)。

CASE2 现在,在另一方面, #define JOIN_(lhs, rhs) JOIN__(lhs, rhs) =>这里,没有标记粘贴操作,宏参数是完全宏扩展它们代入宏体之前。因此,它将允许lhs和rhs扩展并且用扩展参数JOIN__(FOO,AB)来调用,因此现在JOIN__具有令牌粘贴操作符,它将简单地连接它的参数FOO和AB即FOOAB。这是做到这一点的适当方式。

CASE3 #define JOIN(lhs, rhs) JOIN_(lhs, rhs) =>与CASE2相同。

希望能解释一下多级扩展范例背后的原因。

ORIGINAL 预处理运算符##提供了一种在宏扩展期间连接实际参数的方法。如果替换文本中的参数与##相邻,则该参数将被实际参数替换,将删除##和周围的空白区域,并重新扫描结果。例如,宏粘贴连接它的两个参数:

#define paste(front, back) front ## back 

so paste(name, 1) creates the token name1. 
+0

我知道令牌粘贴操作符。我在询问这种多级扩展范例,而不是级联 –

+0

@AndréFratelli:请查找“多级扩展范例”的更新答案。希望它解释你在找什么。 –

+0

真棒回答,真的很彻底。 –

1

##记号化操作不计算(宏展开)它的参数。函数式的宏扩展do然而,评估参数,这就是为什么你得到预期(评估)输出的第一种情况。

技术上,宏JOIN_是不必要的,因为在JOINlhsrhs扩大JOIN__时被评估。这将是足够的:

#define JOIN(lhs, rhs) JOIN__(lhs, rhs) 
#define JOIN__(lhs, rhs) lhs##rhs 
+0

我真的找不到你错了的情况,但为什么代码会这样写呢? –

+0

请参阅我的编辑。我包含了原始代码 –

+0

@AndréFratelli - 谁知道?人们会做一些奇怪的事情......但我认为没有任何理由将代码编写成这种方式。 – owacoder