宏定义中的#,##
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. Java并发编程:锁的释放

    Java并发编程:锁的释放 */--> code {color: #FF0000} pre.src {background-color: #002b36; color: #839496;} Ja ...

  2. Django @csrf_exempt不能在类视图中工作(Django @csrf_exempt not working in class View)

    我在Django 1.9中有一个使用SessionMiddleware的应用程序.我想在同一个项目中为这个应用程序创建一个API,但是在做一个POST请求时,它不能使用@csrf_exempt注释. ...

  3. Windows消息理解(系统消息队列,进程消息队列,非队列消息)

    // ====================Windows消息分类==========================在Windows中,消息分为以下三类:标准消息——除WM_COMMAND之外,所 ...

  4. Vue切换页面时中断axios请求

    一.概述 在Vue单页面开发过程中,遇到这样的情况,当我切换页面时,由于上一页面请求执行时间长,切换到该页面时,还未执行完,这时那个请求仍会继续执行直到请求结束,此时将会影响页面性能,并且可能对现在页 ...

  5. k3 cloud中如何把一个账套中的单据部署到另一个账套中

    打开bos,依次点击->解决方案->部署包管理 填写部署包名称并点击下一步 选择需要部署的单据并点击下一步 确定后点击下一步: 点击完成 找到部署路径会成一个部署包: 部署:打开部署包安装 ...

  6. Taro -- Swiper的图片由小变大3d轮播效果

    Swiper的图片由小变大3d轮播效果 this.state = ({ nowIdx:, swiperH:'', imgList:[ {img:'../../assets/12.jpg'}, {img ...

  7. Java Annotation 刷课笔记(一)

    1.什么是注解? 1.1.Annotation是从JDK5.0引入的新技术 1.2.Annotation的作用: 不是程序本身,可以对程序作出解释(这一点,和注释没什么区别) 可以被其他程序(比如编译 ...

  8. c#中DataTable和DataSet区别

    你可以把DataTable和DataSet看做是数据容器,比如你查询数据库后得到一些结果,可以放到这种容器里,那你可能要问:我不用这种容器,自己读到变量或数组里也一样可以存起来啊,为什么用容器? 原因 ...

  9. wepy相关

    1.根据官方文档: $ npm install @wepy/cli -g # 全局安装 WePY CLI 工具 $ wepy init standard myproj # 使用 standard 模板 ...

  10. 【JavaScript】DOM之BOM

    BOM 1.BOM是什么 提供了独立页面内容,与浏览器相关的一系列对象,管理窗口之间通信 2.Window对象 具有双重角色,对象即是允许JS访问浏览器窗口的一个对象,和ECMAScript规范中的G ...