一个C语言宏展开问题

一个令人比较迷惑的问题,学C语言好多年,今天终于搞明白,记之。

1 #define cat(x,y)  x ## y
2 #define xcat(x,y) cat(x,y)
3 cat(cat(1,2),3) //为什么不是 123?
4 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

有兴趣的话,可以看下面一些宏替换问题:

 1 #define X 3
 2 #define Y X*2
 3 #undef X
 4 #define X 2
 5 int z=Y; // z = 4
 6 
 7 #define hash_hash # ## #
 8 #define mkstr(a) # a
 9 #define in_between(a) mkstr(a)
10 #define join(c, d) in_between(c hash_hash d)
11 char p[] = join(x, y); // 等同于char p[] = "x ## y";
12 
13 #define connect(x) i ## x
14 #define connect2(x) connect(x)
15 #define s(a) a
16 #define is(a) a
17 int i2=2;
18 printf("%d/n", connect(s(1)));/*connect的形参x是##的操作数,故不展开它对应的实参s,直接连接记号i和实参序列s(1),得到is(1),继续替换得到最后结果1*/
19 printf("%d/n", connect2(s(2)));/*connect2的形参x不是##的操作数,故先展开它对应的实参s,再用展开结果2替换之,得到connect(2),继续替换得到最后结果i2*/
20 
21 #define TEST(a,b) /
22 do {/
23 printf(#a "=%d/n", a); /
24 printf(#b "=%d/n", b); /
25 } while (0)
26 // 为什么要写在 do {} while (0) 中呢?而不是{}

 

本文参考资料,强烈推荐一看:

http://blog.csdn.net/huyansoft/archive/2008/05/26/2484297.aspx
http://www.mikeash.com/pyblog/friday-qa-2010-12-31-c-macro-tips-and-tricks.html

转载于:https://www.cnblogs.com/9sheng/archive/2011/03/22/2684247.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值