宏展开的步骤

本文探讨了宏展开的复杂过程,涉及多次替换、嵌套宏展开的规则,以及避免无穷展开的策略。通过实例详细解释了宏替换从左到右进行、深度展开顺序以及特殊运算符如"#"和"##"的处理方式。总结了宏展开的几个关键规律,包括展开方向、嵌套限制和宏自展开的处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

宏展开在预处理中进行,觉得无非就是替换,很简单。最近遇到一个问题,看了一些关于嵌套宏展开的文章,处处皆学问啊!

这三篇是英文的。

The Macro Expansion Process

Macro ssession of TheGNU C Preprocessor

macro expansion algorithm

 中文资料:

The Macro Expansion Process 宏展开过程 (上面英文版本The Macro Expansion Process的翻译)

http://cpp.ezbty.org/myfiles/boost/libs/wave/doc/macro_expansion_process.html

 这个很好!

http://learn.akae.cn/media/ch21s02.html#id2798306

将其中一个章节摘录入下:

2.4. 宏展开的步骤

以上举的宏展开的例子都是最简单的,有些宏展开的过程要做多次替换,例如:

#define sh(x) printf("n" #x "=%d, or %d\n",n##x,alt[x])
#define sub_z  26
sh(sub_z)

sh(sub_z)要用sh(x)这个宏定义来展开,形参x对应的实参是sub_z,替换过程如下:

  1. #x要替换成"sub_z"

  2. n##x要替换成nsub_z

  3. 除了带###运算符的参数之外,其它参数在替换之前要对实参本身做充分的展开,所以应该先把sub_z展开成26再替换到alt[x]x的位置。

  4. 现在展开成了printf("n" "sub_z" "=%d, or %d\n",nsub_z,alt[26]),所有参数都替换完了,这时编译器会再扫描一遍,再找出可以展开的宏定义来展开,假设nsub_zalt是变量式宏定义,这时会进一步展开。

再举一个例子:

#define x 3
#define f(a) f(x * (a))
#undef x
#define x 2
#define g f
#define t(a) a
t(t(g)(0) + t)(1);

展开的步骤是:

  1. 先把g展开成f再替换到#define t(a) a中,得到t(f(0) + t)(1);

  2. 根据#define f(a) f(x * (a)),得到t(f(x * (0)) + t)(1);

  3. x替换成2,得到t(f(2 * (0)) + t)(1);。注意,一开始定义x为3,但是后来用#undef x取消了x的定义,又重新定义x为2。当处理到t(t(g)(0) + t)(1);这一行代码时x已经定义成2了,所以用2来替换。还要注意一点,现在得到的t(f(2 * (0)) + t)(1);中仍然有f,但不能再次根据#define f(a) f(x * (a))展开了,f(2 * (0))就是由展开f(0)得到的,这里面再遇到f就不展开了,这样规定可以避免无穷展开(类似于无穷递归),因此我们可以放心地使用递归定义,例如#define a a[0]#define a a.member等。

  4. 根据#define t(a) a,最终展开成f(2 * (0)) + t(1);。这时不能再展开t(1)了,因为这里的t就是由展开t(f(2 * (0)) + t)得到的,所以不能再展开了。

[总结]

宏展开的几个规律:

1. 宏替换是从左到右处理的。

2. 宏嵌套时,展开顺序是深度展开(跟遍历二叉树的深度遍历意思类似,与广度遍历区分)

3. 遇到“#”, “##” 时,按广度展开,第二遍扫描,再深度展开。

4. 嵌套自己的,只展开一次,第二次保持该宏(可能是要用一个函数定义了)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值