宏定义中的#,##
1. 宏中的参数前面使用一个#,预处理器会把这个参数转换为一个字符数组    
2.记号粘贴操作符(token paste operator): ##       
    “##”是一种分隔连接方式,它的作用是先分隔,然后进行强制连接       
实参代入宏文本后,实参之前或之后遇到#或##,实参不再展开 
                                     

①一个较长的宏定义可以分成若干行,这需要在待续的行末尾加上一个反斜杠符”\”

#define TUP_ASSERT(__expression) do {\

if (!( __expression )) \

{ \

SYSLOG_LEGACY(EAaSysLogSeverityLevel_Error,"<TUP ASSERTION FAILED> (%s) file: %s line:%u", #__expression,__FILE__, __LINE__); \

AaErrorAssertion(#__expression,__FILE__, __LINE__); \

} \

} while(0)

②在替换文本中,参数名以#作为前缀则结果将被扩展为由实际参数替换该参数的带引号的字符串

#define dprint(expr) printf(#expr “ =%g\n”, expr)

使用语句dprint(x/y),该宏将被扩展为

printf(“x/y” “= %g\n”, x/y);

③预处理运算符##为宏展开提供参数连接的作用

#define paste(front, back) front ##back

宏调用paste(name,1)的结果将建立记号name1

④宏展开顺序大致可以归结为:

第一步:首先用实参代替形参,将实参代入宏文本中

第二步:如果实参也是宏,则展开实参

第三步:最后继续处理宏替换后的宏文本,如果仍包含宏,则继续展开

注意:如果在第二步,实参代入宏文本后,实参之前或之后遇到#或##,实参不再展开

例1:

#define cat(a,b) a ## b

宏调用:cat(cat(1, 2), 3) 的展开顺序为:

cat(cat(1, 2), 3) -->cat(1, 2) ## 3  -->cat(1, 2)3

cat(1,2)仍是宏,但后面是##,不再展开,结果为:cat(1, 2)3

例2:

#define  cat(a,b)   a ## b

#define  xcat(x, y)   cat(x, y)

宏调用 xcat(xcat(1, 2), 3) 的展开顺序为:

xcat(xcat(1,2), 3) -->cat(xcat(1, 2), 3) -->cat(cat(1, 2), 3) -->cat(1 ## 2, 3) --> 1 ##2 ## 3 -->123

务必注意参数的处理顺序,第二步在第三步前面执行,第一层宏展开后,实参是宏,则首先处理实参的宏展开,即使宏替换后本身也是宏。在例2中,虽然也生成的cat(cat(1, 2), 3),但是是首先执行里面的cat(1, 2), 所以结果不一样。例2中以下顺序是错的:

xcat(xcat(1,2), 3) --> cat(xcat(1, 2), 3) --> xcat(1, 2) ## 3 -->xcat(1, 2)3

在Linux测试结果:

使用gcc –E 编译可以只做预处理:源文件hepeng.c:

*****************************************************************************

#define cat(a,b) a ## b

#define xcat(x,y) cat(x,y)

int  main()

{

   cat(cat(1,2),3);

   xcat(xcat(1,2),3);

   xcat(cat(1,2),3);

    return 0;

}

*****************************************************************************

[penhe@hzling23 program]$gcc -E hepeng.c |more

*****************************************************************************

# 1 "hepeng1.c"

# 1 "<built-in>"

# 1 "<command line>"

# 1 "hepeng1.c"

int main()

{

    cat(1,2)3;

    123;

    123;

    return 0;

}

*****************************************************************************

 

boost 中包含了许多奇技淫巧的代码,这里分析宏的自身迭代

以这样的宏代码调用

1
BOOST_PP_ENUM_PARAMS(4, typename T)

它的宏展开为

1
typename T0 , typename T1 , typename T2 , typename T3

这在boost中被多用于简化代码量 比如 boos::function 中

下面来分析这类宏的具体实现

宏1:

1
#define BOOST_PP_ENUM_PARAMS(count, param) BOOST_PP_REPEAT(count, BOOST_PP_ENUM_PARAMS_M, param)

首先看看 BOOST_PP_ENUM_PARAMS_M 的作用

宏2:

1
#define BOOST_PP_REPEAT BOOST_PP_CAT(BOOST_PP_REPEAT_, BOOST_PP_AUTO_REC(BOOST_PP_REPEAT_P, 4))

由宏2,可以看出,宏1展开为

1
BOOST_PP_CAT(BOOST_PP_REPEAT_, BOOST_PP_AUTO_REC(BOOST_PP_REPEAT_P, 4))(count, BOOST_PP_ENUM_PARAMS_M, param)

BOOST_PP_AUTO_REC(BOOST_PP_REPEAT_P, 4) 其实就是 1,这个是一个定值,在目前,我们不必去深究 那么, BOOST_PP_CAT 就可以展开为

1
BOOST_PP_REPEAT_1(count, BOOST_PP_ENUM_PARAMS_M, param)

BOOST_PP_REPEAT_1 是一个非常简单的迭代宏

1
2
3
4
5
6
7
# define BOOST_PP_REPEAT_1(c, m, d) BOOST_PP_REPEAT_1_I(c, m, d)
# define BOOST_PP_REPEAT_1_I(c, m, d) BOOST_PP_REPEAT_1_ ## c(m, d)
# define BOOST_PP_REPEAT_1_0(m, d)
# define BOOST_PP_REPEAT_1_1(m, d) m(2, 0, d)
# define BOOST_PP_REPEAT_1_2(m, d) BOOST_PP_REPEAT_1_1(m, d) m(2, 1, d)
# define BOOST_PP_REPEAT_1_3(m, d) BOOST_PP_REPEAT_1_2(m, d) m(2, 2, d)
... ...

所以

1
BOOST_PP_REPEAT(count, BOOST_PP_ENUM_PARAMS_M, param)

展开就等于

1
2
3
4
BOOST_PP_ENUM_PARAMS_M(2, 0, param)
BOOST_PP_ENUM_PARAMS_M(2, 1, param)
... ...
BOOST_PP_ENUM_PARAMS_M(2, count - 1, param)

BOOST_PP_ENUM_PARAMS_M 自然也是一个宏

1
# define BOOST_PP_ENUM_PARAMS_M(z, n, param) BOOST_PP_COMMA_IF(n) param ## n

所以就有

1
2
BOOST_PP_ENUM_PARAMS_M(2, 0, param) // 展开 第一个参数不要, 其实这个参数被用于优化的目的
BOOST_PP_COMMA_IF(0) param ## 0

BOOST_PP_COMMA_IF 是一个这样的宏,如果参数非0,那么打印出逗号,否则就不打印逗号

1
2
3
4
5
6
7
8
9
10
11
12
13
#define BOOST_PP_COMMA_IF(cond) BOOST_PP_IF(cond, BOOST_PP_COMMA, BOOST_PP_EMPTY)()
#define BOOST_PP_IF(cond, t, f) BOOST_PP_IIF(BOOST_PP_BOOL(cond), t, f)
#define BOOST_PP_IIF(bit, t, f) BOOST_PP_IIF_I(bit, t, f)
#define BOOST_PP_IIF_I(bit, t, f) BOOST_PP_IIF_ ## bit(t, f)
# define BOOST_PP_IIF_0(t, f) f
# define BOOST_PP_IIF_1(t, f) t
#define BOOST_PP_BOOL(x) BOOST_PP_BOOL_I(x)
# define BOOST_PP_BOOL_I(x) BOOST_PP_BOOL_ ## x
# define BOOST_PP_BOOL_0 0
# define BOOST_PP_BOOL_1 1
# define BOOST_PP_BOOL_2 1
... ...
# define BOOST_PP_BOOL_256 1

可以看出,宏多用穷举

 

参考:

https://www.xuebuyuan.com/2198272.html

 https://blog.csdn.net/buye1986/article/details/45100339

可以看出,宏多用穷举

C\C++语言中的宏多重展开和递归展开的更多相关文章

  1. C语言学习笔记--C语言中的宏定义

    1. C 语言中的宏定义 (1)#define 是预处理器处理的单元实体之一(因此,预处理器只是简单的进行替换,并不(2)#define 定义的宏可以出现在程序的任意位置(包括函数体的内部)(3)#d ...

  2. C语言中的宏展开

    #include<stdio.h> #define f(a,b) a##b #define g(a) #a #define h(a) g(a) int main() { printf(,) ...

  3. C语言中的宏定义

    目录(?)[-] 简单宏定义 带参数的宏 运算符 运算符 宏的通用属性 宏定义中圆括号 创建较长的宏 较长的宏中的逗号运算符 宏定义中的do-while循环do 空操作的定义 预定义宏 C语言中常用的 ...

  4. C语言中的宏总结

    宏定义分为两种: 1.变量式宏定义,如 #define abc def #define str "string" #define num 100 2.函数式宏定义, #define ...

  5. C语言中的宏

    写好C语言,漂亮的宏定义很重要,使用宏定义可以防止出错,提高可移植性,可读性,方便性 等等.下面列举一些成熟软件中常用得宏定义...... 1,防止一个头文件被重复包含 #ifndef COMDEF_ ...

  6. 20个C语言中常用宏定义总结

    01: 防止一个头文件被重复包含 #ifndef COMDEF_H#define COMDEF_H//头文件内容#endif 02: 重新定义一些类型防止由于各种平台和编译器的不同,而产生的类型字节数 ...

  7. c语言中使用宏,需要注意的的几点

    使用#define来定义一些宏,进行一些简洁的替换甚至一些带参数的宏,在linux c代码中很常见,说明它很好.很有用, 但是它也有一些复杂的规矩和陷阱需要注意,下面我记录一些,仅供参考. 1.当使用 ...

  8. Visual Studio属性配置中使用宏

    在学习C语言的时候,我们曾经遇到过一个宏的概念.宏的作用机理本质上是宏的展开,C语言中的宏的用法也有很多种(水其实很深...),不过从感觉上来讲,人们大致上会在以下的场景中,利用宏来解决一些窘境:一是 ...

  9. C语言中可变参数的原理——printf()函数

    函数原型: int printf(const char *format[,argument]...) 返 回 值: 成功则返回实际输出的字符数,失败返回-1. 函数说明: 使用过C语言的人所再熟悉不过 ...

随机推荐

  1. linux的mv、cp 命令

    用mv命令1.作用mv命令来为文件或目录改名或将文件由一个目录移入另一个目录中.该命令等同于DOS系统下的ren和move命令的组合.它的使用权限是所有用户.2.格式mv [options] 源文件或 ...

  2. java中访问修饰符public,private,protected,friendly的作用域

    在修饰的元素上没有写任何的访问修饰符,则表示friendly. 作用域 当前类 同一包中 子孙类 其他包中 public √ √ √ √ protected √ √ √ × private √ x x ...

  3. python数据结构:pandas(3)

    一.pandas数据操作: 1.处理缺失数据 (1)判断是否存在缺失值 ser_obj.isnull(),df_obj.isnull() (2)dropna:丢弃缺失数据 (3)fillna:填充缺失 ...

  4. IBM公司面试题

    进入IBM差不多是每一个IT人的梦想.IBM公司向来以高素质人才作为企业持续竞争力的保证,所以经常出一些千奇百怪的面试题,来考验一个人的综合能力,以下是5道IBM曾经出过的面试题,看看你能作出几道:  ...

  5. jquery data的用法

    jquery data和 jquery attr, js getAttribute 有着本质的区别,并且无法用$(el).data('property')的方法,去获取$(el).attr('data ...

  6. 使用axios发送ajax请求

    1.安装 npm install axios 2.在Home.vue中引入 import axios from 'axios' export default {   name: 'Home',   c ...

  7. 从零开始学MySQL(四)

    上节连接:https://www.cnblogs.com/RajXie/p/10880809.html 上节说到,在创建表的同时,需要给出列的定义.列的定义可展开如下: 列名 列的数据类型 列的一些其 ...

  8. 北京太速科技股份有限公司产品手册V201903020

    如果您无法正常查看,请点击在线浏览                                           如果您无法正常查看,请点击在线浏览 了解更多产品信息,请扫描二维码,期待您的关注 ...

  9. 磁盘IO性能优化-实践

    RAID卡缓存策略调整 原因详解 操作实例 I/O 调度算法 文件系统journal 磁盘挂载参数 操作实例 性能数据对比 RAID卡缓存策略调整 可以将RAID卡缓存策略由No Write Cach ...

  10. Netty学习--第一章 JDK自带的BIO

    一.什么是BIO BIO是传统的通信技术,在BIO通信模型中,客户端发送请求给服务器,服务器每次都是会单独创建一个线程来监控客户端的请求,会为每个客户端创建一个线程来处理请求.当前服务器处理完成后,通 ...