宏展开在预处理中进行,觉得无非就是替换,很简单。最近遇到一个问题,看了一些关于嵌套宏展开的文章,处处皆学问啊!
这三篇是英文的。
Macro ssession of TheGNU C Preprocessor
中文资料:
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,替换过程如下:
-
#x要替换成"sub_z"。 -
n##x要替换成nsub_z。 -
除了带
#和##运算符的参数之外,其它参数在替换之前要对实参本身做充分的展开,所以应该先把sub_z展开成26再替换到alt[x]中x的位置。 -
现在展开成了
printf("n" "sub_z" "=%d, or %d\n",nsub_z,alt[26]),所有参数都替换完了,这时编译器会再扫描一遍,再找出可以展开的宏定义来展开,假设nsub_z或alt是变量式宏定义,这时会进一步展开。
再举一个例子:
#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);
展开的步骤是:
-
先把
g展开成f再替换到#define t(a) a中,得到t(f(0) + t)(1);。 -
根据
#define f(a) f(x * (a)),得到t(f(x * (0)) + t)(1);。 -
把
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等。 -
根据
#define t(a) a,最终展开成f(2 * (0)) + t(1);。这时不能再展开t(1)了,因为这里的t就是由展开t(f(2 * (0)) + t)得到的,所以不能再展开了。
[总结]
宏展开的几个规律:
1. 宏替换是从左到右处理的。
2. 宏嵌套时,展开顺序是深度展开(跟遍历二叉树的深度遍历意思类似,与广度遍历区分)
3. 遇到“#”, “##” 时,按广度展开,第二遍扫描,再深度展开。
4. 嵌套自己的,只展开一次,第二次保持该宏(可能是要用一个函数定义了)。
宏展开的深度解析
本文探讨了宏展开的复杂过程,涉及多次替换、嵌套宏展开的规则,以及避免无穷展开的策略。通过实例详细解释了宏替换从左到右进行、深度展开顺序以及特殊运算符如"#"和"##"的处理方式。总结了宏展开的几个关键规律,包括展开方向、嵌套限制和宏自展开的处理。
396

被折叠的 条评论
为什么被折叠?



