本文整理自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中 #与##的神奇用法的更多相关文章

  1. #define中 #与##的神奇用法linux学习 (转)

    #define中 #与##的神奇用法linux学习 (转) #define f(a,b) a##b #define d(a) #a #define s(a) d(a) void main( void ...

  2. Linux中“!"的神奇用法

    前言 实际上,不起眼的“!”在linux中有着很多让你惊叹的妙用.本文就来细数那些“!”的神奇用法. 执行上一条命令 例如,在执行完上面一条命令后,可以使用下面的方式再次执行上一条命令: $ wher ...

  3. C++语言中std::array的神奇用法总结,你需要知道!

    摘要:在这篇文章里,将从各个角度介绍下std::array的用法,希望能带来一些启发. td::array是在C++11标准中增加的STL容器,它的设计目的是提供与原生数组类似的功能与性能.也正因此, ...

  4. #define命令的一些高级用法

    =========================================================== define中的三个特殊符号:#,##,#@ ================= ...

  5. 【opencv基础】Rect类的神奇用法

    前言 最近看github上源码发现对两个cv::Rect使用相与(&)操作,猛地感觉自己蒙啦,Rect类还有这种神奇用法?!翻看opencv官网Rect类,果然如此! opencv中Rect类 ...

  6. C++ #define,typedef,using用法区别

    一.#define #define 是宏定义命令,宏定义就是将一个标识符定义为一个字符串,源程序中的该标识符均以指定的字符串来代替,是预编译命令,因此会在预编译阶段被执行 1.无参宏定义 无参宏的宏名 ...

  7. Java中的Socket的用法

                                   Java中的Socket的用法 Java中的Socket分为普通的Socket和NioSocket. 普通Socket的用法 Java中的 ...

  8. ecshop中foreach的详细用法归纳

    ec模版中foreach的常见用法. foreach 语法: 假如后台:$smarty->assign('test',$test); {foreach from=$test item=list ...

  9. matlab中patch函数的用法

    http://blog.sina.com.cn/s/blog_707b64550100z1nz.html matlab中patch函数的用法——emily (2011-11-18 17:20:33) ...

随机推荐

  1. Win32 程序在启动时激活前一个启动程序的窗口

    UWP 程序天生单实例.当然,新 API (10.0.17134)开始也提供了多实例功能.不过,传统 Win32 程序可就要自己来控制单实例了. 本文介绍简单的几个 Win32 方法调用,使 Win3 ...

  2. 《DSP using MATLAB》示例Example7.10

    代码: ws1 = 0.2*pi; wp1 = 0.35*pi; wp2 = 0.65*pi; ws2 = 0.8*pi; As = 60; tr_width = min((wp1-ws1), (ws ...

  3. mix deps HEX_HTTP_CONCURRENCY=1 HEX_HTTP_TIMEOUT=120 timeout

    mix  deps.get  timeout 问题: If this happens consistently, adjust your concurrency and timeout setting ...

  4. hapi lab测试框架简单使用

    1. 依赖安装 yarn init yarn add lab code 2. 基本模式 const Lab = require('lab'); const Code = require('code') ...

  5. Linux下gdb线程的调试

    多线程的调试命令 1.info threads: 这条命令显示的是当前可调试的所有线程,GDB会给每一个线程都分配一个ID.前面有*的线程是当前正在调试的线程. 2.thread ID: 切换到当前调 ...

  6. 使用Maven运行Solr(翻译)

    Solr是一个使用开源的搜索服务器,它采用Lucene Core的索引和搜索功能构建,它可以用于几乎所有的编程语言实现可扩展的搜索引擎. Solr的虽然有很多优点,建立开发环境是不是其中之一.此博客条 ...

  7. 【备忘】windows环境下20行php代码搞定音频裁剪

    先上图,由于最近的需求需要对语音文件进行处理,所以抽空研究了下php处理音/视频文件的处理,简单的demo处理,截取一个音频文件的前20秒,并保存新的媒体文件. 操作步骤: ①在此站点下载所需的辅助程 ...

  8. 黄聪:免费C#反编译软件工具。Reflector已经out了,试试ILSpy吧

    转载自:http://www.cnblogs.com/JamesLi2015/archive/2011/09/08/2170519.html Reflector是.NET开发中必备的反编译工具.即使没 ...

  9. Mybatis数据的增删改查

    数据: Student{id int,name String ,age int} 配置mybatis-config.xml <?xml version="1.0" encod ...

  10. C程序花括号嵌套层次统计(新)

    [问题描述] 编写程序,统计给定的C源程序中花括号的最大嵌套层次,并输出花括号嵌套序列,该程序没有语法错误. 注意:1)源程序注释(/* … */)中的花括号应被忽略,不参与统计.2)源程序中的字符串 ...