C\C++语言中的宏多重展开和递归展开
①一个较长的宏定义可以分成若干行,这需要在待续的行末尾加上一个反斜杠符”\”
#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 |
|
它的宏展开为
1 |
|
这在boost中被多用于简化代码量 比如 boos::function 中
下面来分析这类宏的具体实现
宏1:
1 |
|
首先看看 BOOST_PP_ENUM_PARAMS_M 的作用
宏2:
1 |
|
由宏2,可以看出,宏1展开为
1 |
|
BOOST_PP_AUTO_REC(BOOST_PP_REPEAT_P, 4) 其实就是 1,这个是一个定值,在目前,我们不必去深究 那么, BOOST_PP_CAT 就可以展开为
1 |
|
BOOST_PP_REPEAT_1 是一个非常简单的迭代宏
1 |
|
所以
1 |
|
展开就等于
1 |
|
BOOST_PP_ENUM_PARAMS_M 自然也是一个宏
1 |
|
所以就有
1 |
|
BOOST_PP_COMMA_IF 是一个这样的宏,如果参数非0,那么打印出逗号,否则就不打印逗号
1 |
|
可以看出,宏多用穷举
https://www.xuebuyuan.com/2198272.html
https://blog.csdn.net/buye1986/article/details/45100339
可以看出,宏多用穷举
C\C++语言中的宏多重展开和递归展开的更多相关文章
- C语言学习笔记--C语言中的宏定义
1. C 语言中的宏定义 (1)#define 是预处理器处理的单元实体之一(因此,预处理器只是简单的进行替换,并不(2)#define 定义的宏可以出现在程序的任意位置(包括函数体的内部)(3)#d ...
- C语言中的宏展开
#include<stdio.h> #define f(a,b) a##b #define g(a) #a #define h(a) g(a) int main() { printf(,) ...
- C语言中的宏定义
目录(?)[-] 简单宏定义 带参数的宏 运算符 运算符 宏的通用属性 宏定义中圆括号 创建较长的宏 较长的宏中的逗号运算符 宏定义中的do-while循环do 空操作的定义 预定义宏 C语言中常用的 ...
- C语言中的宏总结
宏定义分为两种: 1.变量式宏定义,如 #define abc def #define str "string" #define num 100 2.函数式宏定义, #define ...
- C语言中的宏
写好C语言,漂亮的宏定义很重要,使用宏定义可以防止出错,提高可移植性,可读性,方便性 等等.下面列举一些成熟软件中常用得宏定义...... 1,防止一个头文件被重复包含 #ifndef COMDEF_ ...
- 20个C语言中常用宏定义总结
01: 防止一个头文件被重复包含 #ifndef COMDEF_H#define COMDEF_H//头文件内容#endif 02: 重新定义一些类型防止由于各种平台和编译器的不同,而产生的类型字节数 ...
- c语言中使用宏,需要注意的的几点
使用#define来定义一些宏,进行一些简洁的替换甚至一些带参数的宏,在linux c代码中很常见,说明它很好.很有用, 但是它也有一些复杂的规矩和陷阱需要注意,下面我记录一些,仅供参考. 1.当使用 ...
- Visual Studio属性配置中使用宏
在学习C语言的时候,我们曾经遇到过一个宏的概念.宏的作用机理本质上是宏的展开,C语言中的宏的用法也有很多种(水其实很深...),不过从感觉上来讲,人们大致上会在以下的场景中,利用宏来解决一些窘境:一是 ...
- C语言中可变参数的原理——printf()函数
函数原型: int printf(const char *format[,argument]...) 返 回 值: 成功则返回实际输出的字符数,失败返回-1. 函数说明: 使用过C语言的人所再熟悉不过 ...
随机推荐
- mac下安装php zookeeper扩展
安装步骤 php-zookeeper依赖libzookeeper,所以需要先安装libzookeeper 安装libzookeeper cd /usr/local/src/ wget http://m ...
- DotNetCore知识栈
#..NET Core提供的特性 1.开源.免费 2.轻量级.跨平台 3.组件化.模块化.IOC+Nuget.中间件 4.高性能 5.统一了MVC和WebAPI编程模型 a) 比如:ASP.NET ...
- RESTful API 设计总结
RESTful API 设计总结 @(技术-架构)[API, 规范, 设计] RESTful的接口设计风格应用的越来越广泛,包括Spring Cloud等微服务架构平台之间的调用都是以RESTful设 ...
- node搭建个人博客promise警告解除
警告 (node:8500) UnhandledPromiseRejectionWarning: undefined (node:8500) UnhandledPromiseRejectionWarn ...
- 缓存字节流-写coppy方法
package ba; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io. ...
- Hadoop本地模式搭建
官方文档,不同版本修改url地址中的数字即可 http://hadoop.apache.org/docs/r2.7.2/hadoop-project-dist/hadoop-common/Single ...
- GROUP BY关键字优化
1.group by实质是先排序后进行分组,遵照索引建的最佳左前缀 2.当无法使用索引列,增大max_length_for_sort_data参数的设置+增大sort_buffer_size参数的设置 ...
- 关于Mysql 修改密码的记录
初次安装后完毕,使用管理员身份进入cmd界面, 输入" mysql -u root -p",出现"Enter password:",直接回车输入" s ...
- springboot poi
pom.xml <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</a ...
- scrapy处理post请求的传参和日志等级
一.Scrapy的日志等级 - 在使用scrapy crawl spiderFileName运行程序时,在终端里打印输出的就是scrapy的日志信息. - 日志信息的种类: ERROR : 一般错误 ...