预处理运算符“ ##”

时间:2019-01-26 07:41:03

标签: c c-preprocessor preprocessor-directive

在学习“预处理程序运算符”时,我在书中发现了一个定义:

//Listen for app to POST Credential app.post('/', async function(request, response) { console.log('Request Body: ',request.body); var Credential = request.body; //Validate Credential var userId = await validateCredential(Credential) //Waits until userId comes //Get Authorization Token var firebaseAuthToken = await getToken(userID) //waits until Token comes //Return Token for POST Response response.set('Content-Type','Text'); response.end(firebaseAuthToken); });

调用#define CONCAT(x,y) x##y可获得所需的输出CONCAT(a,b)。但是ab不会给出CONCAT(a,CONCAT(b,c))而是给出奇数输出。

Book通过解释说,替换列表中没有扩展在替换列表中abc之前或之后的宏参数。因此,##扩展为CONCAT(a,CONCAT(b,c)),由于没有名为aCONACT(b,c)的宏,因此无法进一步扩展。 好的,我明白了,但本书还进一步提到,可以通过定义仅调用第一个宏的第二个宏来解决此问题。例子

aCONCAT

现在写#define CONCAT2(x,y) CONCAT(x,y)可以得到所需的列表CONCAT2(a,CONCAT2(b,c))

但是如何?我认为abc将被CONCAT2(a,CONCAT2 (b,c))取代,而CONCAT(a,CONCAT2(b,c))将进一步扩展为aCONCAT2(b,c)。现在没有命名为aCONCAT2的宏,就像在第一种情况下一样,那么期望的输出又如何呢?

这是CONCAT2(a,CONCAT2 (b,c))正常工作的证明。

enter image description here

enter image description here 看到编译器不会显示任何错误。除了有关使用getch()函数的警告。

1 个答案:

答案 0 :(得分:1)

如果有

#define CONCAT(x,y)   x##y
#define CONCAT2(x,y)  CONCAT(x,y)

然后当预处理程序看到

CONCAT(a,CONCAT(b,c))

它知道CONCAT(x,y)的替换列表是x ## y,因此它将用 a y 替换 x 。 strong>和 CONCAT(b,c)。唯一的问题是,在替换之前,它会展开 a 和/或 CONCAT(b,c)吗? a 不是宏,因此无法扩展,并且在替换列表x ## y中,y之前是##,因此对参数CONCAT(b,c)不会进行扩展。因此,替换操作无需扩展即可完成,替换列表将变为a ## CONCAT(b,c),然后在检查更多宏之前,它将处理##,替换列表将变为aCONCAT(b,c)。

如果预处理器看到

CONCAT2(a,CONCAT2(b,c))

它知道CONCAT2(x,y)的替换列表是CONCAT(x,y),因此它将用 a 替换 x y CONCAT2(b,c)。唯一的问题是,在替换之前,它会展开 a 和/或 CONCAT2(b,c)吗? a 不是宏,因此无法扩展,并且在替换列表CONCAT(x,y)中, y 之前不能带有#或##,也不能跟在后面##,因此 CONCAT2(b,c)在替换前已完全展开。因此, CONCAT2(b,c)扩展为CONCAT(b,c),后者扩展为b ## c,无法进一步扩展,因此将 y 替换为由b ## c。替换列表x ## y变成a ## b ## c,或者变成ab ## c,然后变成abc,或者变成a ## bc,然后变成abc。

如果预处理器看到

CONCAT2(a,CONCAT(b,c))

它知道CONCAT2(x,y)的替换列表是CONCAT(x,y),因此它将用 a 替换 x y CONCAT(b,c)。唯一的问题是,在替换之前,它会展开 a 和/或 CONCAT(b,c)吗? a 不是宏,因此无法扩展,并且在替换列表CONCAT(x,y)中, y 之前不能带有#或##,也不能跟在后面##,因此 CONCAT(b,c)在替换前已完全展开。因此,将 CONCAT(b,c)扩展为b ## c,无法进一步扩展,因此 y 被b ## c替换,替换列表为x ## y变为a ## b ## c,然后变为ab ## c,然后变为abc,或者变为a ## bc,然后变为abc。

如果预处理器看到

CONCAT(a,CONCAT2(b,c))

它知道CONCAT(x,y)的替换列表是x ## y,因此它将用 a y 替换 x 。 strong>和 CONCAT2(b,c)。唯一的问题是,在替换之前,它会展开 a 和/或 CONCAT2(b,c)吗? a 不是宏,因此无法扩展,并且在替换列表x ## y中,y之前是##,因此对参数CONCAT2(b,c)不会进行扩展。因此,替换操作无需扩展即可完成,替换列表变为a ## CONCAT2(b,c),然后在检查更多宏之前,它会处理##,替换列表变为aCONCAT2(b,c)。

您可能会认为

#define CONCAT2(x,y)  CONCAT(x,y)

表示

CONCAT2(x,y)应该与CONCAT(x,y)相同

但请记住:

  1. CONCAT(x,y)的替换列表是x ## y,并且因为x后面是##,y之前是##,所以当预处理程序看到CONCAT宏的实例时,它将替换前不展开与x或y对应的参数。但是,CONCAT2(x,y)的替换列表是CONCAT(x,y),替换中的x或y都不能以#或##开头或以##开头,因此当预处理器看到CONCAT2宏的实例时,它将在替换之前完全扩展参数中的所有宏。

  2. 参数的宏扩展(如果允许)在替换之前发生。因此在CONCAT2(a,CONCAT(b,c))中,在替换之前先扩展CONCAT(b,c)参数。所以我们得到CONCAT2(a,b ## c)而不是CONCAT(a,CONCAT(b,c))。