#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) ... 
随机推荐
- xml的读取(曾删改)
			获取XML 得到 需要查询的字段名 private string GetXml(string TableName) { try { string TbName = TableName.Split('_ ... 
- C#/.NET 匿名函数会捕获变量,并延长对象的生命周期
			小伙伴在一次垃圾回收中,发现对象并没有被回收掉,而注释掉一句代码后它便能够回收. 这究竟是为什么? 不关心探索过程的就直接拉到最后看结论吧! 探索 测试代码是这样的: private void O ... 
- python正则的使用
			python的正则是通过re模块的支持 匹配的3个函数 match :只从字符串的开始与正则表达式匹配,匹配成功返回matchobject,否则返回none: re.match(pattern, st ... 
- c#开发的程序安装时动态指定windows服务名称
			转自:http://www.jb51.net/article/30549.htm 前段时间由于项目的需求,要在Windows里把同样的组件制作成多个不同名称的服务,这些服务完成类似的功能,仅需要修改业 ... 
- Hadoop 2.7.4 关闭与启动
			环境说明:[root@hadp-master sbin]# hadoop versionHadoop 2.7.4 一. 脚本封装全部一起启动.关闭1.1 启动[root@hadp-master sbi ... 
- python 与时间有关的操作
			python保存时间戳文件 import time # ISOTIMEFORMAT='%Y-%m-%d %X' ISOTIMEFORMAT='%Y-%m-%d' t= time.strftime( I ... 
- bzoj 3158 千钧一发——网络流
			题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3158 发现偶数之间一定满足第二个条件:奇数之间一定满足第一个条件 ( \( (2m+1)^{ ... 
- (转)Android 读取联系人(详细)
			import java.io.InputStream; import org.json.JSONArray; import org.json.JSONException; import org.jso ... 
- laravel里面的一些变量
			laravel5里面一些配置,比如数据库,debug的,实际上在项目的.env里面定义过了 //forge是默认值 'database' => env('DB_DATABASE', 'forge ... 
- (转)Inno Setup入门(五)——添加readme文件
			本文转载自:http://blog.csdn.net/yushanddddfenghailin/article/details/17250771 这个实现起来很简单,就是在[files]段中的某个预先 ... 
