了解宏扩展

时间:2015-09-26 19:49:36

标签: c macros

我最近看到了一些像这样的代码:

#define JOIN(lhs, rhs)   JOIN_(lhs, rhs)
#define JOIN_(lhs, rhs)  JOIN__(lhs, rhs)
#define JOIN__(lhs, rhs) lhs##rhs

我测试了代码,调用如下:

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文件

2 个答案:

答案 0 :(得分:1)

## tokenize运算符不会对其参数进行求值(宏扩展)。类似于函数的宏扩展执行评估参数,这就是为什么你得到第一种情况的预期(评估)输出。

从技术上讲,宏JOIN_是不必要的,因为在展开lhs时会评估rhs中的JOINJOIN__。这就足够了:

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

答案 1 :(得分:1)

修改

  

3.9.6论据Prescan

     

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

     

调用其他字符串化或连接的宏的宏。如果   参数是字符串化或连接的,预扫描不会发生。   如果要扩展宏,则将其字符串化或连接   扩展,你可以通过使一个宏调用另一个宏来做到这一点   进行字符串化或连接。

例如,如果你有

#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 =&gt;因为它有一个令牌粘贴操作符,它将连接那些在替换之前不完全宏扩展的宏参数。 - &GT;这是一个不好的扩展方式,首先我们不知道,它将传递给它的参数是什么,它不会等待它的扩展,它只会连接它。

因此,当你调用JOIN__(Foo, JOIN(A,B));时,它将不允许JOIN(A,B)扩展,它会将它连接到FOOJOIN(A,B)。

<强> CASE2 另一方面,现在, #define JOIN_(lhs, rhs) JOIN__(lhs, rhs) =&gt;这里没有令牌粘贴操作符,宏参数在被替换为宏体之前完全是宏扩展的。因此,它将允许lhs和rhs扩展并用扩展参数JOIN__(FOO,AB)调用,因此现在,JOIN__有一个标记粘贴操作符,它将简单地连接它的参数FOO和AB,即FOOAB。这是适当的方式。

<强> CASE3 #define JOIN(lhs, rhs) JOIN_(lhs, rhs) =&gt;与CASE2相同。

希望,它解释了多层次扩张范式背后的原因

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

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

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