#define中 #与##的神奇用法
本文整理自csdn。
#define f(a,b) a##b
#define d(a) #a
#define s(a) d(a)
void main( void )
{
puts(d(f(a,b)));
puts(s(f(a,b)));
}
输出结果:
f(a,b)
ab
分析: ##把两个符号连起来
#a指把a当成符号,就是把#后面的看成字符串
# 和 ## 操作符是和#define宏使用的. 使用# 使在#后的首个参数返回为一个带引号的字符串. 例如, 命令
#define to_string( s ) # s
将会使编译器把以下命令
cout < < to_string( Hello World! ) < < endl;
理解为
cout < < "Hello World!" < < endl;
使用##连结##前后的内容. 例如, 命令
#define concatenate( x, y ) x ## y
...
int xy = 10;
...
将会使编译器把
cout < < concatenate( x, y ) < < endl;
解释为
cout < < xy < < endl;
理所当然,将会在标准输出处显示'10'.
puts(d(f(a,b))); ----> 因为d宏中的参数是另外一个宏,且带##,所以作为参数的宏不展开,相当于
puts(#f(a,b));----->puts("f(a,b)");
puts(s(f(a,b))); ----> 因为s宏中的参数是另外一个宏,但不带##,所以作为参数的宏先展开,相当于
puts(s(ab));----->puts(d(ab));---->puts(#ab);---->puts("ab");
#define f(a,b) a##b
#define d(a) #a --》 以"#"开头的,直接替换,不展开:immediately replaced by the unexpanded actual argument
#define s(a) d(a) --》 非以"#"开头的,先展开,再替换,也就是一般的情况
所以就两种情况:
1,不以"#"开头的,先展开参数a,然后是替换代码:puts(s(f(a,b)));-->puts(s(ab))-->puts(d(ab))-->puts("ab")
2,以"#"开头的,直接替换,不展开:puts(d(f(a,b)))-->puts("f(a,b)")
#include <stdio.h>
#define DIRECT_LITERAL(a) #a
#define INDIRECT_LITERAL(a) DIRECT_LITERAL(a)
int main(void)
{
puts(DIRECT_LITERAL(INDIRECT_LITERAL(a + b)));
puts(INDIRECT_LITERAL(DIRECT_LITERAL(a + b)));
return 0;
}
这其实从编译角度的展开归约也可以理解啊。
以上代码第一种情况,当预编译器看到DIRECT_LITERAL后查到它是宏定义,定义为#a,此时后面的参数部分就会以#a的形式生成到源文件中。也就是说,预编译后的源文件中,替代第一条语句的就是:
puts("INDIRECT_LITERAL(a + b)");输出则是INDIRECT_LITERAL(a + b)
而对于第二条语句,当预编译器看到INDIRECT_LITERAL后查到它是宏定义,定义为DIRECT_LITERAL(a),这时先把它作为DIRECT_LITERAL(DIRECT_LITERAL(a
+ b))的形式暂存起来,你也可以理解为这个状态是语法树的当中一个叶结点。然后再分析后面的DIRECT_LITERAL后面的参数部分,即:DIRECT_LITERAL(a + b),同样,预编译器会将它归约为"a + b"的形式。这样对于里面的DIRECT_LITERAL(a + b)的形式就完全确定下来了,那么这个值就可以充当叶子结点,即它底下不会再有结点。然后再回到刚才那个状态,DIRECT_LITERAL("a + b")最后就是"\"a + b\""。所以这里输出是"a + b"。
值得注意的是#a是将参数a转为字符串形式。所以像DIRECT_LITERAL(a)的展开形式是字符串常量"a"
那么DIRECT_LITERAL("a")展开就是"\"a\""。
#include <stdio.h>
#define DIRECT_LITERAL(a)
#a
#define INDIRECT_LITERAL(a)
DIRECT_LITERAL(a)
#define DIRECT_CAT(a,
b) a##b
#define INDIRECT_CAT(a,
b) DIRECT_CAT(a, b)
int main(void)
{
puts(DIRECT_LITERAL(INDIRECT_LITERAL(a + b)));
puts(INDIRECT_LITERAL(DIRECT_LITERAL(a + b)));
puts(INDIRECT_LITERAL(DIRECT_CAT(INDIRECT_CAT(a, b), INDIRECT_CAT(c, d))));
puts(INDIRECT_LITERAL(INDIRECT_CAT(DIRECT_CAT(a, b), DIRECT_CAT(c, d))));
return 0;
}
#define中 #与##的神奇用法的更多相关文章
- #define中 #与##的神奇用法linux学习 (转)
#define中 #与##的神奇用法linux学习 (转) #define f(a,b) a##b #define d(a) #a #define s(a) d(a) void main( void ...
- Linux中“!"的神奇用法
前言 实际上,不起眼的“!”在linux中有着很多让你惊叹的妙用.本文就来细数那些“!”的神奇用法. 执行上一条命令 例如,在执行完上面一条命令后,可以使用下面的方式再次执行上一条命令: $ wher ...
- C++语言中std::array的神奇用法总结,你需要知道!
摘要:在这篇文章里,将从各个角度介绍下std::array的用法,希望能带来一些启发. td::array是在C++11标准中增加的STL容器,它的设计目的是提供与原生数组类似的功能与性能.也正因此, ...
- #define命令的一些高级用法
=========================================================== define中的三个特殊符号:#,##,#@ ================= ...
- 【opencv基础】Rect类的神奇用法
前言 最近看github上源码发现对两个cv::Rect使用相与(&)操作,猛地感觉自己蒙啦,Rect类还有这种神奇用法?!翻看opencv官网Rect类,果然如此! opencv中Rect类 ...
- C++ #define,typedef,using用法区别
一.#define #define 是宏定义命令,宏定义就是将一个标识符定义为一个字符串,源程序中的该标识符均以指定的字符串来代替,是预编译命令,因此会在预编译阶段被执行 1.无参宏定义 无参宏的宏名 ...
- Java中的Socket的用法
Java中的Socket的用法 Java中的Socket分为普通的Socket和NioSocket. 普通Socket的用法 Java中的 ...
- ecshop中foreach的详细用法归纳
ec模版中foreach的常见用法. foreach 语法: 假如后台:$smarty->assign('test',$test); {foreach from=$test item=list ...
- matlab中patch函数的用法
http://blog.sina.com.cn/s/blog_707b64550100z1nz.html matlab中patch函数的用法——emily (2011-11-18 17:20:33) ...
随机推荐
- Docker环境下eShopOnContainers部署
从头开始学eShopOnContainers--Visual Studio 2017环境配置 https://www.cnblogs.com/dusthunter/p/9973815.html 一.安 ...
- BZOJ3590 [Snoi2013]Quare
题意 4.20四川芦山地震发生后,抗震救灾委员会接到一个紧急任务,四川省给该委员会发了一份地图,这份地图给出了该省一些城市的情况:任两个城市是用一条或多条公路连接起来的,也可以没有公路连接,但是每个城 ...
- mock的使名用一(生成随机数据)
Mock.Random 是一个工具类,用于生成各种随机数据. Mock.Random 的方法在数据模板中称为『占位符』,书写格式为 @占位符(参数 [, 参数]) . var Random = Moc ...
- IDEA使用介绍
https://blog.csdn.net/qq_27093465/article/details/77449117
- bzoj4557侦查守卫
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4557 树形DP.和“河流”有点像,也有一个类似“承诺”的东西. 就是用 f 表示当前节点向下 ...
- string源码实现分析
最近写hashtable的实现的时候用模板类的思想,在普通int,long,double类型的时候测试时没问题的,当用到string的时候,一直有问题. 实现的equal函数是比较粗暴的使用两者所有对 ...
- golang之交叉编译设置
俺的环境,os x,目的,生成64位linux的elf文件 直接下载osx的包就可以,不需要特意去下载源码包,我的go目录是~/golang/go cd ~/golang/go/srcGOOS=lin ...
- 数据结构与算法JavaScript描述——栈的使用
有一些问题特别适合用栈来解决.本节就介绍几个这样的例子. 1) 数制间的相互转换 可以利用栈将一个数字从一种数制转换成另一种数制.假设想将数字n 转换为以b 为基数的数字,实现转换的算法如下. 使 ...
- mysql命令之二:查看mysql版本的四种方法
1:在终端下:mysql -V. 以下是代码片段: [shengting@login ~]$ mysql -V mysql Ver 14.7 Distrib 4.1.10a, for redhat-l ...
- Mysql auto_increment总结
一.为什么InnoDB表要建议用自增列做主键 我们先了解下InnoDB引擎表的一些关键特征: InnoDB引擎表是基于B+树的索引组织表(IOT): 每个表都需要有一个聚集索引(clustered i ...