深入 了解 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相关推荐

  1. 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. ...

  2. Angular1.4.6框架简单读取数据库信息并渲染完成news新闻文章列表以及detail详情页功能(小试牛刀)

    项目结构 css/angular-common.css table tr td:first-child {/**背景图片*/width: 200px;height: 100px;/**居中填满*/ba ...

  3. Vue.js框架简单读取数据库信息并渲染完成news新闻文章列表以及detail详情页功能(小试牛刀)

    项目结构 news.html(新闻列表文件) <!doctype html> <html lang="en"> <head><meta c ...

  4. SetGet and MACRO

    为什么80%的码农都做不了架构师?>>>    Set&Get 配合private是c++ class里面常用的. 这样很大程度上可以对数据的存取进行控制. 最近接触了大量的 ...

  5. 异常-----freemarker.template.TemplateException: Error executing macro: write

    freemarker自定义标签 假如你现在还在为自己的技术担忧,假如你现在想提升自己的工资,假如你想在职场上获得更多的话语权,假如你想顺利的度过35岁这个魔咒,假如你想体验BAT的工作环境,那么现在请 ...

  6. 模型评估指标micro avg、macro avg和weighted avg的计算方式及区别

    模型评估指标micro avg.macro avg和weighted avg的计算方式及区别-技术圈

  7. stitching detail输出的dot图含义

    如果利用opencv里面提供的stitching detail的话. 输入参数: stitching_detail --save_graph a.dot 1.png 2.png 其中a.dot 文件中 ...

  8. not enough actual parameters for macro 'min'(QT与vs2010)

    解决方案见以下: qdatetime.h:"min"宏的实参不足 | 浏览:73 | 更新:2015-01-06 12:36 百度经验:jingyan.baidu.com 最近用V ...

  9. 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, ...

最新文章

  1. iOS组件化开发实践
  2. 走近虚拟机——McAfee研究员孙冰谈虚拟机技术和虚拟机安全
  3. deque,list,queue,priority_queue
  4. JAVA知识基础(十一):异常
  5. 悖论对计算机科学影响,数学和计算机科学的核心逻辑悖论
  6. php字节怎么转化成字符串,php将utf-8(3字节)字符串转换成字节
  7. 信息学奥赛一本通(2050:【例5.20】字串包含)
  8. 数字滤波器(一)--IIR与FIR的基本结构与MATLAB实现
  9. 【Flink】Could not connect to BlobServer at address
  10. 前端到底是自学好还是培训好?
  11. 全局变量只能初始化不能赋值
  12. javascript中在链表中向前(向后)移动n个节点
  13. java 中怎么打印一个日历_日历打印用java实现
  14. python-常见的语法错误
  15. IEC 61131 标准系列
  16. C程序实例1--个人通讯录管理系统
  17. 浅聊基于MUI框架的混合开发
  18. android 平板原笔迹,iPad劲敌八:原笔迹输入你行吗?
  19. 54:第五章:开发admin管理服务:7:人脸入库流程;人脸登录流程;浏览器开启视频调试模式(以便能够在本机的不安全域名的情况下,也能去开启摄像头);
  20. 用html做七巧板的方法,七巧板制作教程 七巧板的制作方法

热门文章

  1. 揭阳电网要求计算机二级吗,2018年3月广东省揭阳市计算机等级考试考务通知
  2. android视频聊天桌面小窗口怎么实现,android视频通话悬浮窗的适配
  3. 【最新版】Win10 Java jdk14.0.2安装及环境变量配置
  4. mysql慢查询日志平时开启吗_MySQL开启慢查询日志功能的方法
  5. cdh 安装_CDH 中为spark 安装 python3
  6. 1、计算机图形学——2D变换与齐次坐标
  7. linux 服务启动依赖,linux下的系统服务介绍——init、systemd
  8. 添加javascript代码:_JavaScript的使用
  9. 推荐一个非常好用的Chrome扩展应用,用于美化Json字符串
  10. rocketmq源码解析之name启动(一)