一个C语言宏展开问题

转自一个C语言宏展开问题


一个令人比较迷惑的问题,学C语言好多年,今天终于搞明白,记之。
-------------------------------------------------------------
#define cat(x,y)  x ## y
#define xcat(x,y) cat(x,y)
cat(cat(1,2),3) //为什么不是 123?
xcat(xcat(1,2),3) //结果为什么是 123?
-------------------------------------------------------------

要解答这个问题,首先看一下预处理过程的几个步骤:
1) 字符集转换(如三联字符)
2) 断行连接 /
3) 注释处理, /* comment */,被替换成空格
4) 执行预处理命令,如 #include、#define、#pragma、#error等
5) 转义字符替换
6) 相邻字符串拼接
7) 将预处理记号替换为词法记号

在这里主要关注第4步,即如何展开函数宏。(其他步骤和本文关系不大,但对于理解预处理过程是十分重要的)宏函数替换展开,规则可简单总结如下:在展开当前宏函数时,如果形参有#(字符串化操作)或##(记号连接操作)则不进行宏参数的展开,否则先展开宏参数,再展开当前宏(就像先计算函数中的参数,然后调用函数一样)。回头看最初的问题,则两个宏展开过程如下:
-------------------------------------------------------------
cat(cat(1,2),3)
=> cat(1,2) ## 3    // cat(x,y)  x##y参数前有##操作,参数不展开
=> cat(1,2)3        // K&R中说,)3 是一个不合法记号,不展开
-------------------------------------------------------------
xcat(xcat(1,2),3)
=> xcat(cat(1,2),3)  //xcat(x,y) cat(x,y)参数前无#,##操作,则先展开参数
=> xcat(1 ## 2,3)
=> xcat(12,3)
=> cat(12,3)
=> 12 ## 3
=> 123
-------------------------------------------------------------

有兴趣的话,可以看下面一些宏替换问题:
-------------------------------------------------------------
#define X 3
#define Y X*2
#undef  X
#define X 2
int z=Y; // z = 4
-------------------------------------------------------------
#define hash_hash # ## #
#define mkstr(a) # a
#define in_between(a) mkstr(a)
#define join(c, d) in_between(c hash_hash d)
char p[] = join(x, y); // 等同于char p[] = "x ## y";
-------------------------------------------------------------
#define connect(x) i ## x
#define connect2(x) connect(x)
#define s(a) a
#define is(a) a
int i2=2;
printf("%d/n", connect(s(1)) );/*connect的形参x是##的操作数,故不展开它对应的实参s,直接连接记号i和实参序列s(1),得到is(1),继续替换得到最后结果1*/
printf("%d/n", connect2(s(2)) );/*connect2的形参x不是##的操作数,故先展开它对应的实参s,再用展开结果2替换之,得到connect(2),继续替换得到最后结果i2*/
-------------------------------------------------------------
#define TEST(a,b) /
do {/
printf(#a "=%d/n", a); /
printf(#b "=%d/n", b); /
} while (0)


原文地址:https://www.cnblogs.com/noble/p/4144039.html