本文整理自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. IOS SEL (@selector) 原理及使用总结(二)

    SEL消息机制工作原理是什么 引用下面文章: 我们在之前有提到,一个类就像一个 C 结构.NSObject 声明了一个成员变量: isa. 由于 NSObject 是所有类的根类,所以所有的对象都会有 ...

  2. Python学习-赋值、浅copy和深copy

    Python Copy: 在Python语言中,分为浅拷贝和深拷贝两种形式,也就是官方文档中的Shadow copy和Deep copy.在对简单的对象(object)进行复制时,两者没有区别,如下面 ...

  3. win8.1下安装双系统ubuntu14.04.3

    一.去ubuntu官网下载长期支持版的系统,64位还是32位由物理内存而定,4G以下用32位,4G以上(包括4G)使用64位. 二.若64位的系统,下载下来的文件名应该是ubuntukylin-14. ...

  4. ballerina 学习十二 变量

    ballerina 有两种方式进行变量的定义,类型加上名称以及初始值.,使用var 关键字 简单例子 代码 import ballerina/io; // 全局public 变量,使用类型定义 pub ...

  5. Nomad 了解

    Introduction to Nomad Welcome to the intro guide to Nomad! This guide is the best place to start wit ...

  6. kong k8s 安装 以及可视化管理界面

    1. git  clone $ git clone git@github.com:Mashape/kong-dist-kubernetes.git $ cd kong-dist-kubernetes ...

  7. [LeetCode系列]3元素最近和问题的O(n^2)解法

    给定一个整数数组(长度不小于3) 和 一个目标值, 从数组中找出3个元素, 使得它们的和与目标值最接近, 返回这个和. 可以认为每个输入的组合都是只有唯一解的. 解法思路参考: Finding thr ...

  8. 完美版js金钱正则表达式校验

    <!doctype html> <html lang="en">  <head>   <meta charset="UTF-8& ...

  9. 对SQLite数据库操作 操作db文件

    sqlite数据库就是一个DB文件.  程序每操作一次数据库都要读一次 .DB  文件 .  这个文件就是这个SQLite数据库. 如果需要依赖包的可以联系我 工具类: package com.hot ...

  10. linux网络编程、系统编程

    http://blog.csdn.net/lianghe_work/article/category/2871247