转自一个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)

一个C语言宏展开问题的更多相关文章

  1. c语言 预处理的使用 宏展开下的#,##

    1. #include   包含头文件 2.define 宏定义(可以理解为替换,不进行语法检查) 写法 #define 宏名 宏体  加括号 #define ABC (5+3) #define AB ...

  2. C宏展开的几个注意事项

    前阵子仔细重新研究了一下C的宏展开.总结起来,有以下几个主要规则: 每次宏展开的结果会被重复扫描,直到没有任何可展开的宏为止. 每展开一个宏,都会记住这次展开,在这个宏展开的结果及其后续展开中,不再对 ...

  3. 第一个C语言编译器是怎样编写的?

    首先向C语言之父Dennis MacAlistair Ritchie致敬! 当今几乎所有的实用的编译器/解释器(以下统称编译器)都是用C语言编写的,有一些语言比如Clojure,Jython等是基于J ...

  4. C中宏展开问题

    C中宏展开问题 简单记录一下碰到的问题. #define STR(x) #x 我们知道使用上面的宏可以将x转换为字符串"x". 但是如果这样用: #define NUM 3 #de ...

  5. 【做中学】第一个 Go 语言程序:漫画下载器

    原文地址: 第一个 Go 语言程序:漫画下载器: https://schaepher.github.io/2020/04/11/golang-first-comic-downloader 之前学了点 ...

  6. 你知道第一个C语言C++编译器是如何诞生的吗?

    当今几乎所有的实用的编译器/解释器(以下统称编译器)都是用 C 语言编写的,有一些语言比如 Clojure,Jython 等是基于 JVM 或者说是用 Java 实现的,IronPython 等是基于 ...

  7. 第一个C语言程序

    从第一个C语言程序了解C语言 了解关键字 了解函数 注释 C语言的执行流程 标识符 C语言的学习重难点 从第一个C语言程序了解C语言 上图是一个在控制台上显示“Hello, World!”的C语言源代 ...

  8. php调用一个c语言写的接口问题

    用php调用一个c语言写的soap接口时,遇到一个问题:不管提交的数据正确与否,都无法请求到接口 1.用php标准的soap接口去请求 2.拼接xml数据去请求 以上两种方式都不正确 解决办法:php ...

  9. 机器学习(一) 从一个R语言案例学线性回归

    写在前面的话 按照正常的顺序,本文应该先讲一些线性回归的基本概念,比如什么叫线性回归,线性回规的常用解法等.但既然本文名为<从一个R语言案例学会线性回归>,那就更重视如何使用R语言去解决线 ...

随机推荐

  1. sparkSQL1.1入门

    http://blog.csdn.net/book_mmicky/article/details/39288715 2014年9月11日,Spark1.1.0忽然之间发布.笔者立即下载.编译.部署了S ...

  2. 2 WPF之XMAL----XMAL概览

    转载:http://blog.csdn.net/fwj380891124/article/details/8085458 微软为了把开发模式从网络开发移植到桌面开发和富媒体网络程序的开发上,微软创造了 ...

  3. 在知乎回答的一个问题:C#初学者以后往WP开发走,还是往unity3d走?哪个更有前景呢

    问题原文:我是一个C#初学者,想问个问题,我以后是该往WP开发方向走呢还是学习Unity3D往游戏开发,哪个更有前景呢,个人感觉未来几年iOS,WP,Java会三足鼎立,WP现在有潜力,但是U3D现在 ...

  4. JavaScript生成GUID的多种算法小结

    全局唯一标识符(GUID,Globally Unique Identifier)也称作 UUID(Universally Unique IDentifier) . GUID是一种由算法生成的二进制长度 ...

  5. ###《Effective STL》--Chapter4

    点击查看Evernote原文. #@author: gr #@date: 2014-09-14 #@email: forgerui@gmail.com Chapter4 迭代器 Topic 26: i ...

  6. C 语言 查找一个字符串2在字符串1中出现的次数

    #include <stdio.h> #include <windows.h> int main() { ], b[]; char *temp; ; memset( a, ); ...

  7. Jsp页面里引入一个javascript文件,在jsp的onclick里怎么添加脚本文件里的方法

    举个简单例子:首先新建use.js文件内容:function show(){ alert("aa");} 建html文件跟use.js放同目录下: <html>< ...

  8. error C2664 转换错误汇总[转]

    vs2005提示 error C2664: “CWnd::MessageBoxW”: 不能将参数 1 从“const char [17]”转换为“LPCTSTR”. 在用vs2005编写mfc程序的时 ...

  9. linux内核启动参数

    Linux内核启动参数   Console Options                         参数 说明 选项 内核配置/文件   console=Options 用于说明输出设备 tt ...

  10. ViewState压缩

    /// <summary> ///CompressViewState 的摘要说明 /// </summary> public class CompressViewState:S ...