C 预处理运算符“##&引用;

C 预处理运算符“##&引用;,c,c-preprocessor,preprocessor-directive,C,C Preprocessor,Preprocessor Directive,在学习“预处理器运算符”时,我在一本书中找到了一个定义: #定义CONCAT(x,y)x##y 调用CONCAT(a,b)将给出所需的输出ab。但是CONCAT(a,CONCAT(b,c))不会给出abc而是给出奇数输出 书中解释说,替换列表中的##前面或后面的宏参数在替换时不会展开。因此CONCAT(a,CONCAT(b,c))展开为acontact(b,c),无法进一步展开,因为没有名为acontat的宏。 好的,我明白了,但书中进一步提到,这个问题可以通过定义第二个宏来解决,该宏只调用第一

在学习“预处理器运算符”时,我在一本书中找到了一个定义:

#定义CONCAT(x,y)x##y

调用CONCAT(a,b)将给出所需的输出
ab
。但是
CONCAT(a,CONCAT(b,c))
不会给出
abc
而是给出奇数输出

书中解释说,替换列表中的
##
前面或后面的宏参数在替换时不会展开。因此
CONCAT(a,CONCAT(b,c))
展开为
acontact(b,c)
,无法进一步展开,因为没有名为
acontat
的宏。 好的,我明白了,但书中进一步提到,这个问题可以通过定义第二个宏来解决,该宏只调用第一个宏。范例

#定义CONCAT2(x,y)CONCAT(x,y)

编写
CONCAT2(a,CONCAT2(b,c))
现在可以生成所需的列表
abc

但是怎么做呢?我认为
CONCAT2(a,CONCAT2(b,c))
将被
CONCAT2(a,CONCAT2(b,c))
取代,后者进一步扩展到
acocat2(b,c)
。现在没有名为
acocat2
的宏,就像第一种情况一样,那么所需的输出是如何产生的呢

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

确保编译器不会显示任何错误。除了用于使用getch()函数的警告。

如果您有

#define CONCAT(x,y)   x##y
#define CONCAT2(x,y)  CONCAT(x,y)
然后当预处理器看到

CONCAT(a,CONCAT(b,c))
CONCAT2(a,CONCAT2(b,c))
CONCAT2(a,CONCAT(b,c))
CONCAT(a,CONCAT2(b,c))
它知道CONCAT(x,y)的替换列表是x##y,因此它将用a替换x,用CONCAT(b,c)替换y。唯一的问题是,在替换之前,它会扩展到a和/或CONCAT(b,c)a不是宏,因此无法展开,在替换列表x###y中,y前面有###,因此无法展开参数CONCAT(b,c)。因此,替换是在不展开的情况下完成的,替换列表变成了一个###CONCAT(b,c),然后在检查更多宏之前,它会处理##,替换列表变成acocat(b,c)

如果预处理器看到

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

如果预处理器看到

CONCAT(a,CONCAT(b,c))
CONCAT2(a,CONCAT2(b,c))
CONCAT2(a,CONCAT(b,c))
CONCAT(a,CONCAT2(b,c))
它知道CONCAT2(x,y)的替换列表是CONCAT(x,y),因此它将用a替换x,用CONCAT(b,c)替换y。唯一的问题是,在替换之前,它会扩展到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,CONCAT(b,c))
CONCAT2(a,CONCAT2(b,c))
CONCAT2(a,CONCAT(b,c))
CONCAT(a,CONCAT2(b,c))
它知道CONCAT(x,y)的替换列表是x##y,因此它将用a替换x,用CONCAT2(b,c)替换y。唯一的问题是,在替换之前,它是否会扩展a和/或CONCAT2(b,c)a不是宏,因此无法展开,在替换列表x###y中,y前面有###,因此不会展开参数CONCAT2(b,c)。因此,替换是在不展开的情况下完成的,替换列表变成了一个###CONCAT2(b,c),然后在检查更多宏之前,它会处理##,替换列表变成一个cat2(b,c)

你可能会这么想

#define CONCAT2(x,y)  CONCAT(x,y)
意味着

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

但请记住:

  • CONCAT(x,y)的替换列表是x###y,并且由于x后面跟着##,y前面跟着###,因此当预处理器看到CONCAT宏的实例时,它不会在替换之前展开对应于x或y的参数。但是,CONCAT2(x,y)的替换列表是CONCAT(x,y),替换中的x和y前面都没有#或###或后面跟着##,因此当预处理器看到CONCAT2宏的实例时,它将在替换之前完全展开参数中的any宏

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


  • 每个问题请回答一个问题。因为这两个问题都与##运算符有关,所以我把它们放在一起。我是否应该将另一个问题作为不同的问题发布?是的,理想情况下,将第二个问题移动到一个新问题中,只留下第一个问题。
    用一些例子来说明。
    实际上,学习它的最简单方法就是玩它。或者读一些好的技巧,比如,很好的解释,斯图尔特·利特尔。