【转】一个C语言宏展开问题

转自   http://www.cnblogs.com/9sheng/archive/2011/03/22/2684247.html

一个令人比较迷惑的问题,学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.youkuaiyun.com/huyansoft/archive/2008/05/26/2484297.aspx
http://www.mikeash.com/pyblog/friday-qa-2010-12-31-c-macro-tips-and-tricks.html

### 回答1: sourceinsight是一款常用的代码编辑工具,它支持C/C++、Java、Python等多种编程语言代码编辑查看。在sourceinsight中,宏展开是一种功能,它可以帮助我们在编写代码的过程中更方便地查看理解宏的展开效果。 宏(Macro)是一种在程序中定义并重复使用的代码片段,在预处理阶段会被替换为相应的代码。而在sourceinsight中,我们可以通过宏展开功能来查看宏定义在具体代码中展开后的效果。 使用sourceinsight的宏展开功能,我们可以通过以下几个步骤来进操作: 1. 打开sourceinsight,在编辑器中打开宏所在的源文件。 2. 在编辑器中找到需要展开的宏,并将光标定位在宏上。 3. 点击sourceinsight菜单栏中的"编辑" -> "宏展开",或者使用快捷键"Ctrl + Shift +E"来展开宏。 4. 在展开后的代码中,我们可以看到宏在具体位置展开后的代码效果。 通过宏展开功能,我们可以更清晰地了解宏在代码中的展开效果,这对于理解调试代码非常有帮助。同时,宏展开功能也可以帮助我们在宏定义较为复杂的情况下,更方便地查看宏展开后的代码逻辑,提高代码编写的效率。 总之,sourceinsight的宏展开功能是一种非常实用的功能,它能够帮助我们更好地理解调试代码,在编写代码时起到辅助作用,提高代码编写效率。 ### 回答2: SourceInsight是一款功能强大的源代码阅读浏览工具,能够方便地浏览、编辑调试源代码。它支持多种编程语言,并提供了一系列有用的功能来提高开发人员的工作效率。 在SourceInsight中,宏展开是指将源代码中的宏定义展开为实际的代码。宏定义是一种预处理器指令,它可以在编译之前对源代码文本替换。宏定义通常用于简化重复的代码片段,提高程序的可读性可维护性。 要在SourceInsight中展开宏定义,首先需要在源代码中定义宏。可以使用#define指令来定义宏,语法形式如下: #define 宏名 宏值 宏名是定义的宏的名称,宏值是宏的实际替换文本。 在进宏展开时,SourceInsight会根据源代码中的宏定义,将所有使用该宏的地方替换为宏的实际值。展开后的代码可以方便地阅读调试。 要进宏展开,可以使用SourceInsight提供的宏展开功能。在SourceInsight的工具栏中,可以找到一个名为"宏展开器"的按钮。点击该按钮,将打开一个新的窗口,显示当前文件的宏展开结果。 宏展开器窗口的左侧显示源代码中的宏定义,右侧显示宏展开后的代码。可以通过双击宏定义来查看宏展开结果。在宏展开器窗口中,还可以对展开后的代码修改调试。 总之,SourceInsight提供了便捷的宏展开功能,可以方便地查看调试宏展开后的代码。它有助于开发人员理解优化宏的使用,提高工作效率。 ### 回答3: Source Insight是一个强大的源代码浏览编辑工具,而宏展开是其其中的一个功能。 在Source Insight中,宏展开是指将代码中使用的宏进展开,使之变为宏定义所代表的实际代码。在编程过程中,我们经常会使用宏来简化代码增加可读性,但在宏展开之前,代码中的宏只是一串字符,没有实际的含义。 要进宏展开,首先需要在Source Insight的宏文件中定义宏。宏文件是一个文本文件,其中包含了宏的定义相关的命令。在宏文件中,我们可以定义宏以及宏的数,还可以编写一系列的命令来执一些操作,例如查找、替换、插代码等。 当我们在源代码中使用宏时,Source Insight会根据宏文件中的定义来进展开。展开过程中,宏的数会被替换为调用宏时传的值,这样宏定义的代码就会被插到相应的位置。 通过宏展开,我们可以在不改变源代码的情况下增加一些额外的功能。例如,我们可以定义一个宏来自动生成一些重复的代码,或者在特定的位置自动插一些标记,方便我们进代码的调试分析。 综上所述,Source Insight的宏展开功能是一种非常有用的工具,可以帮助程序员简化优化代码的编写阅读。宏展开可以通过宏文件的定义来实现,通过将代码中的宏替换为相应的代码,以达到编写代码的目的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值