c detail of macro
深入 了解 C宏 C 宏很强大,但我们大多只知道它的替换功能,具体细节总是不清楚,现在时候全面了解它了。
测试方式
gcc –E macro.test.c
参考资料
gcc:http://gcc.gnu.org/onlinedocs/cpp/Macros.html
也可以研究一下boost的 MACRO Metaprogram
或看Linux内核的一些宏技巧(比如list定义,once_call, 等等)
宏的细节
形式参数
形参是个有效的 C 标识符,以逗号和可选的空格分割。
The parameters must be valid C identifiers, separated by commas and optionally whitespace.
实际参数
实参是以逗号和可选的空格分割。这导致了宏的一个缺陷,参数不能是 (a,b) 这样的,boost的foreach宏就受到这个限制。
gcc不受这个限制
The arguments is separated by commas and optionally whitespace.
例如:
#define CALL(f,a) f a
CALL(printf, ( "%d" , 3 ) ) ==> printf ( "%d" , 3 )
Stringified
#和##只在宏定义中有效。
# stringified 把字符# 右边的 宏参数转换为字符串 "argument"
example:
#define str(a) #a
str(ADD(x)) ==> "ADD(x)"
Pasted
## pasted 对宏进行参数替换后,去除字符##, 这样就可以实现token合并 example:
#define A abc##def
A ==> abcdef
macro body 展开过程
先进行# stringified操作,再对参数进行替换, 最后执行## pasted 操作。
Simple scan 和 Twice scan
object-like宏 和 function-like但没有参数的宏,或macro body 有 #(stringified ) or ##(pasted) 的macro, 只执行一遍扫描(simple scan)。
否则就要执行两遍扫描。
两篇扫描:
prescan: 对参数进行扫描,并对可以展开的参数进行完全的宏展开。
second scan: 用展开后的参数,对宏体进行展开,对展开后的结果递归进行 完全的宏展开。
simple scan 执行 second scan 一样的过程。
example:
simple sacn:
#define no_param hah
no_param ==> hah
#define UNAME(a) a##__LINE__
UNAME(lidy) ==> lidy__LINE__
#define INC(x) x+1
#define STR(b) #b
STR(INC(x)) ===> "INC(x)"
递归问题
无论是simple scan 或 twice scan的宏展开过程,都不允许对同一宏进行第二次展开。
example:
simple scan:
#define x (4 + y)
#define y (2 * x)
x ==> (4 + y)
==> (4 + (2 * x))
twice scan:
#define a(x) a(x)+1
#define b(x) x+2
b(a(y)) ==>b(a(y)+1)
==>a(y)+1 +2
可变参数的宏
用__VA_ARGS__ 引用可变参数:
#define eprintf(format, ...) fprintf (stderr, format, __VA_ARGS__)
eprintf("abc:%d", 3) ===> fprintf(stderr,"abc:%d", 3)
eprintf("bad") ===> fprintf(stderr,"abc:%d", ) 出错,参数太少了
使用##__VA_ARGS___ 可以处理0参数的情形
#define eprintf(format, ...) fprintf (stderr, format, ##__VA_ARGS__)
eprintf("abc:%d", 3) ===> fprintf(stderr,"abc:%d", 3)
eprintf("bad") ===> fprintf(stderr,"abc:%d")
使用其它名字
#define eprintf(format, args...) fprintf (stderr, format , ##args)
宏不允许重复定义
如果两个宏定义基本一致,是不会报错的。
判断宏一致:4条都要满足
1同是object-or function-like
2 宏体中的token要相同(就是空白分割的token)
3 如果有参数,那么形参要相同
4 有相同的空白处(空白字符数不要求一样和像HTML那样)
相同定义,不报错
#define FOUR (2 + 2)
#define FOUR (2 + 2)
#define FOUR (2 /* two */ + 2)
重复定义错误:
#define FOUR (2 + 2)
#define FOUR ( 2+2 )
#define FOUR ( 2 + 2) //第4条 空白处不对
#define FOUR (2 * 2) //第3条 宏体的TOKEN不同
#define FOUR(score,and,seven,years,ago) (2 + 2) //第一条不符号
宏调用中使用宏指令
Directives Within Macro Arguments
If, within a macro invocation, that macro is redefined,
then the new definition takes effect in time for argument pre-expansion, but the original definition is still used for argument replacement.
宏调用中重定义那个宏,那么新的定义只在参数展开中起作用,外层宏的宏体展开还是使用原先的定义
#define f(x) x x
f (1
#undef f
#define f 2
f)
==>1 2 1 2 f(2 3) ==> 2(2 3)
多行调用一个宏:
Here is an example illustrating this:
#define ignore_second_arg(a,b,c) a; c
ignore_second_arg (foo (),
ignored (),
syntax error);
==> foo (); syntax error
这会导致程序的序号提示错误。
对于宏调用尽量都在一行内完成。
ignore_second_arg (foo (),ignored (), syntax error);
typeof扩展和 embeded statement ({})表达式
减少重复计算,可以使用 gcc的 typeof扩展和 语句表达式
({...}) 对语句进行计算,位于括号中的复合语句的最后一句必需是一个以分号结尾的表达式,它的值将成为这个语句表达式的值。
#define min(X, Y) \
({ typeof (X) x_ = (X); \
typeof (Y) y_ = (Y); \
(x_ < y_) ? x_ : y_; })
简单语句模拟
对于多个语法行的宏定义,建议使用do {...} while (0) 来包裹, 这可以把宏调用当成是一个简单的语句
#define SKIP_SPACES(p, limit) \
{ char *lim = (limit); \
while (p < lim) { \
if (*p++ != ' ') { \
p--; break; }}}
if (*p != 0)
SKIP_SPACES (p, lim);
else ...
将出错。
好的方式是:
#define SKIP_SPACES(p, limit) \
do { char *lim = (limit); \
while (p < lim) { \
if (*p++ != ' ') { \
p--; break; }}} \ while (0)
标签: macro
c detail of macro相关推荐
- glog 编译报错 ERROR macro is defined. Define GLOG_NO_ABBREVIATED_SEVERITIES before including logging.h.
glog 编译报错 ERROR macro is defined. Define GLOG_NO_ABBREVIATED_SEVERITIES before including logging.h. ...
- Angular1.4.6框架简单读取数据库信息并渲染完成news新闻文章列表以及detail详情页功能(小试牛刀)
项目结构 css/angular-common.css table tr td:first-child {/**背景图片*/width: 200px;height: 100px;/**居中填满*/ba ...
- Vue.js框架简单读取数据库信息并渲染完成news新闻文章列表以及detail详情页功能(小试牛刀)
项目结构 news.html(新闻列表文件) <!doctype html> <html lang="en"> <head><meta c ...
- SetGet and MACRO
为什么80%的码农都做不了架构师?>>> Set&Get 配合private是c++ class里面常用的. 这样很大程度上可以对数据的存取进行控制. 最近接触了大量的 ...
- 异常-----freemarker.template.TemplateException: Error executing macro: write
freemarker自定义标签 假如你现在还在为自己的技术担忧,假如你现在想提升自己的工资,假如你想在职场上获得更多的话语权,假如你想顺利的度过35岁这个魔咒,假如你想体验BAT的工作环境,那么现在请 ...
- 模型评估指标micro avg、macro avg和weighted avg的计算方式及区别
模型评估指标micro avg.macro avg和weighted avg的计算方式及区别-技术圈
- stitching detail输出的dot图含义
如果利用opencv里面提供的stitching detail的话. 输入参数: stitching_detail --save_graph a.dot 1.png 2.png 其中a.dot 文件中 ...
- not enough actual parameters for macro 'min'(QT与vs2010)
解决方案见以下: qdatetime.h:"min"宏的实参不足 | 浏览:73 | 更新:2015-01-06 12:36 百度经验:jingyan.baidu.com 最近用V ...
- configure.ac:64: error: possibly undefined macro: AM_ICONV
configure.ac:64: error: possibly undefined macro: AM_ICONV If this token and others are legitimate, ...
最新文章
- iOS组件化开发实践
- 走近虚拟机——McAfee研究员孙冰谈虚拟机技术和虚拟机安全
- deque,list,queue,priority_queue
- JAVA知识基础(十一):异常
- 悖论对计算机科学影响,数学和计算机科学的核心逻辑悖论
- php字节怎么转化成字符串,php将utf-8(3字节)字符串转换成字节
- 信息学奥赛一本通(2050:【例5.20】字串包含)
- 数字滤波器(一)--IIR与FIR的基本结构与MATLAB实现
- 【Flink】Could not connect to BlobServer at address
- 前端到底是自学好还是培训好?
- 全局变量只能初始化不能赋值
- javascript中在链表中向前(向后)移动n个节点
- java 中怎么打印一个日历_日历打印用java实现
- python-常见的语法错误
- IEC 61131 标准系列
- C程序实例1--个人通讯录管理系统
- 浅聊基于MUI框架的混合开发
- android 平板原笔迹,iPad劲敌八:原笔迹输入你行吗?
- 54:第五章:开发admin管理服务:7:人脸入库流程;人脸登录流程;浏览器开启视频调试模式(以便能够在本机的不安全域名的情况下,也能去开启摄像头);
- 用html做七巧板的方法,七巧板制作教程 七巧板的制作方法
热门文章
- 揭阳电网要求计算机二级吗,2018年3月广东省揭阳市计算机等级考试考务通知
- android视频聊天桌面小窗口怎么实现,android视频通话悬浮窗的适配
- 【最新版】Win10 Java jdk14.0.2安装及环境变量配置
- mysql慢查询日志平时开启吗_MySQL开启慢查询日志功能的方法
- cdh 安装_CDH 中为spark 安装 python3
- 1、计算机图形学——2D变换与齐次坐标
- linux 服务启动依赖,linux下的系统服务介绍——init、systemd
- 添加javascript代码:_JavaScript的使用
- 推荐一个非常好用的Chrome扩展应用,用于美化Json字符串
- rocketmq源码解析之name启动(一)