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

    html: <div style="position:relative;">   <input type="password" id=&quo ...

  2. 【MINA】心跳机制

    列上两篇好文章 http://www.cnblogs.com/pricks/p/3832882.html http://blog.csdn.net/cruise_h/article/details/1 ...

  3. sharepoint 删除list里的所有内容

    [System.reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint") $siteUrl = " ...

  4. linux shell if参数

    shell 编程中使用到得if语句内判断参数 –b 当file存在并且是块文件时返回真 -c 当file存在并且是字符文件时返回真 -d 当pathname存在并且是一个目录时返回真 -e 当path ...

  5. WinForm程序中两份mdf文件问题的解决

    在项目中用程序中嵌入mdf文件的方式来进行SQLServer数据库开发非常方便,用来发布开源项目等很方便,点击就可以运行,免部署,特别是在教学中用起来更加方便,老师不用先将数据库文件detach再发给 ...

  6. HW-文件恢复-测试300

    //package t0817; import java.util.*; public class FileRelive {//类名换成Main public static void main(Str ...

  7. Apache配置多个监听端口

    以前做PC上的,都是配置一个端口,整一大堆的虚拟目录: 在 \conf\extra下找到httpd-vhosts.conf这个配置文件,想下面这样配置就行,监听80端口,访问相应的ServerName ...

  8. 关于main函数的定义

     很多人甚至市面上的一些书籍,都使用了void main( ),其实这是错误的.C/C++中从来没有定义过void main( ).C++之父Bjarne Stroustrup在他的主页上的FAQ中明 ...

  9. [数据结构] 成都磨子桥技工学校 Challenge Series

    Challenge 0: 第一次裸写竟然WA了.... Challenge 1: 不想写了.

  10. Jar mismatch! Fix your dependencies的问题(转)

    看到网上有说: 在开发Android项目的时候,有时需要引用多个项目作为library.在引用项目的时候,有时会出现“Jar mismatch! Fix your dependencies”错误. 这 ...