转自一个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. C语言中 指针与结构体

    就像数组一样,指向结构体的指针存储了结构体第一个元素的内存地址.与数组指针一样,结构体的指针必须声明和结构体类型保持一致,或者声明为void类型. 1 2 3 4 5 6 7 8 9 10 11 12 ...

  2. gcc的基础知识

    GCC(GNU Compiler Collection,GNU编译器集合)是一套由GNU工程开发的支持多种编程语言的编译器. 基本用法 在使用Gcc编译器的时候,我们必须给出一系列必要的调用参数和文件 ...

  3. .Net 指定时间段内定时执行的Windows服务(System.Threading.Thread)

    创建一个Windows服务项目:解决方案(右击)——> 添加 ——> 新建项目——>项目类型选择Windows——>模板选择Windows服务 ,如图: 编写Windows服务 ...

  4. 주기적으로 php파일 실행시키기 (PHP 파일 cron 으로 돌리기)

    크론탭에 추가 ]# crontab -e       한시간에 한번씩 원하는 페이지를 실행시키는 코드 0 * * * * wget -O - -q -t 1 http://domain.com ...

  5. mysql同时向一个表中插入多条数据问题!!见详细

    INSERT INTO `表名` (`字段1`,`字段2`,`字段3`,`字段4`) values ('数组1数据1','数组1数据2','数组1数据3','数组1数据4'), ('数组2数据1',' ...

  6. 从NSM到Parquet:存储结构的衍化

    http://blog.csdn.net/dc_726/article/details/41777661 为了优化MapReduce及MR之前的各种工具的性能,在Hadoop内建的数据存储格式外,又涌 ...

  7. pdf压缩之GSview

    今天实验室一个同学在网上投简历,网站要求投稿的简历pdf文件必须在100K以内.简历用的是ModernCV的模板,无论如何设置都在160k左右. 尝试用acrobat的压缩功能,也不能保证在100K以 ...

  8. 20个linux命令行工具监视性能(上)

    对于每一个系统管理员或网络管理员每天监视或调试linux系统的性能问题是一件非常困难的事,在it行业作为一个linux管理员五年之后,我开始知道监视和保持系统启动和运行有多么的困难.由于这个原因,我们 ...

  9. CUDA_矢量相加

    #include<iostream> #define N 10 _ _global_ _ void add(*a,*b,*c) { int tid=blockIdx.x; if(tid&l ...

  10. vbscript multiple line syntax

    Vbscript 如何将输出内容换行? ' VbCrLf represetns Carriage return–linefeed combination, for more information s ...