宏分为两种,一种是 object-like 宏,比如:

#define STR "Hello, World!"

另一种是 function-like 宏,比如:

#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))

对于 function-like 宏,定义时的参数叫 Parameters,比如上面宏 MIN 的参数 X、Y,当调用时,传递的参数叫 Arguments。

宏参数 Arguments

当给宏传递参数 Arguments 时,可以不用全部传递,比如:

MIN(a, b)  ->  ((a) < (b) ? (a) : (b)) // 完全传递

MIN(, b) -> (() < (b) ? () : (b)) // 只传递后一个

MIN(a, ) -> ((a) < () ? (a) : ()) // 只传递前一个

MIN(,) -> (() < () ? () :()) // 这种允许

MIN(, ,) // 报错,接收2个参数,传递了3个

MIN() // 报错,接收2个参数,传递了1个

宏参数 Arguments 的扩展

当向宏中传递参数 Arguments 时,在参数替换到宏里面之前,首先要对参数 Arguments 进行完全的扩展,当参数扩展完毕之后,才将最终扩展的结果替换到宏里面,同时再对整个宏进行扩展。比如调用宏 MIN(MIN(a, b), c),那么首先第一个参数 MIN(a, b)要进行扩展,扩展的结果为 ((a) < (b) ? (a) : (b)),由于第2个参数 c 不用扩展,也就是第1步得到的扩展结果为:

MIN(((a) < (b) ? (a) : (b)), c)

接下来进行第2步扩展,得到的结果为:

((((a) < (b) ? (a) : (b))) < (c) ? (((a) < (b) ? (a) : (b))) : (c))

这样做的一个好处是为了防止宏的嵌套调用,比如有下面一个宏定义:

#define f(x) x

如果有如下调用 f(f(1)),假设第一步不将参数 f(1) 完全展开,就会出现 f(f(1)) 被展开为 f(1),由于间接的自引用,宏不再继续展开,此时得到的结果就为 f(1)。而有了2步扩展,第一步参数 f(1) 被扩展成 1,然后替换后继续扩展,可以得到结果就是1。

自引用宏

一个宏在定义时,如果宏名出现在了宏定义中,那么就是一个自引用宏,比如:

#define foo (4 + foo)

如果调用这个宏 foo,按照上面两步展开的过程,那么将会是一个无限循环的过程。首先 foo 展开为 (4 + foo),然后 (4 + foo) 中的 foo 继续展开成 (4 + foo) 成为 (4 + (4 + foo)),如此继续下去。但是实际上,对于自引用宏,只扩展1次,后面不会再做扩展,也就是说调用宏 foo,最终的扩展结果就是 (4 + foo)。

对于自引用宏的一种特殊情况就是间接自引用,比如有如下宏定义:

#define x (4 + y)
#define y (2 * x)

当调用宏 x 时,首先扩展为 (4 + y),然后对 y 进行扩展,结果为 (4 + (2 * x)),如果此时继续对 x 进行扩展,那么就会无限递归,为了处理这种情况,扩展会在这一步终止,也就是最终结果是 (4 + (2 * x))。

当调用宏 y 时一样,首先扩展为 (2 * x),然后对 x 进行扩展,结果为 (2 * (4 + y)),扩展到这一步也会停止。

参数 Arguments 有字符串化(#)和连接(##)操作

在宏扩展过程当中,当参数 Arguments 有字符串化(#)和连接操作(##)时,参数的扩展就没有第1步。比如有下面宏定义:

#define AFTERX(x) X_ ## x
#define XAFTERX(x) AFTERX(x)
#define TABLESIZE 1024
#define BUFSIZE TABLESIZE

当调用 AFTERX(BUFSIZE) 时,由于参数 BUFSIZE 有连接操作(##),那么它就不会继续扩展成 TABLESIZE,TABLESIZE 继续扩展成1024,而是直接使用,也就是最终的扩展结果是 X_BUFSIZE,而不是 X_10。如果想要得到 X_10 的结果,需要做1次间接宏调用,重新定一个宏 XAFTERX 来调用宏 AFTERX,当调用宏 XAFTERX(BUFSIZE) 时,就会得到结果 X_10。

宏参数(Arguments)的扩展的更多相关文章

  1. js参数arguments的理解

    原文地址:js参数arguments的理解 对于函数的参数而言,如下例子 function say(name, msg){ alert(name + 'say' + msg); } say('xiao ...

  2. C/C++语言中#的神奇作用:把宏参数字符串化/贴合宏参数

    宏中"#"和"##"的用法 一.一般用法   我们使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起. #define STR(s)      #s # ...

  3. C/C++中 # 的神奇作用:把宏参数字符串化/贴合宏参数

    一.一般用法   我们使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起. #define STR(s) #s #define CONS(a,b) int(a##e##b) printf(ST ...

  4. [译]Javascript 参数(arguments)对象

    本文翻译youtube上的up主kudvenkat的javascript tutorial播放单 源地址在此: https://www.youtube.com/watch?v=PMsVM7rjupU& ...

  5. 函数的默认值与动态参数arguments的总结

    在js函数与作用域,了解函数基本概念中,我们发现当函数的实参有一个没有上传的时候,对应的形参time展示的值就是undefined,如下代码所示: <!DOCTYPE html> < ...

  6. C语言:宏参数的字符串化和宏参数的连接

    在宏定义中,有时还会用到#和##两个符号,它们能够对宏参数进行操作. # 的用法 #用来将宏参数转换为字符串,也就是在宏参数的开头和末尾添加引号.例如有如下宏定义: #define STR(s) #s ...

  7. 隐式参数arguments

    类数组对象中(长得像一个数组,本质上是一个对象):arguments 常见的对arguments的操作是三个 获取参数的长度  arguments.length 根据索引值获取某一个参数 argume ...

  8. Confluence 6.15 博客页面(Blog Posts)宏参数

    参数是让你可以用来控制宏的格式和输出的选项.在 Confluence 存储格式或者 Wiki 标记(wikimarkup)中使用的参数名与在宏浏览器中使用的标签名是不同的,在下面我们将会用括号列出  ...

  9. js的隐含参数(arguments,callee,caller)使用方法

    在提到上述的概念之前,首先想说说javascript中函数的隐含参数: arguments arguments 该对象代表正在执行的函数和调用它的函数的参数.[function.]arguments[ ...

随机推荐

  1. java多线程编程(一)

    一.概念基础 进程:正在进行中的程序(直译). 线程:就是进程中一个负责程序执行的控制单元(执行路径) 一个进程中可以多执行路径,称之为多线程. 一个进程中至少要有一个线程. 开启多个线程是为了同时运 ...

  2. iOS中利用CoreTelephony获取用户当前网络状态(判断2G,3G,4G) by徐文棋

    前言: 在项目开发当中,往往需要利用网络.而用户的网络环境也需要我们开发者去注意,根据不同的网络状态作相应的优化,以提升用户体验. 但通常我们只会判断用户是在WIFI还是移动数据,而实际上,移动数据也 ...

  3. [杭电新生赛]hgame-week1-web-wp

    hgame第一周总结 (官方wp更加简洁明了.直入主题,个人比较菜,所以过程繁琐 写好了一直没发qwq(就是懒(bushi) 一.Tetris plus! 题目里说玩到3000分就给flag,还真有点 ...

  4. 类(静态)变量和类(静态)static方法以及main方法、代码块,final方法的使用,单例设计模式

    类的加载:时间 1.创建对象实例(new 一个新对象时) 2.创建子类对象实例,父类也会被加载 3.使用类的静态成员时(静态属性,静态方法) 一.static 静态变量:类变量,静态属性(会被该类的所 ...

  5. js实现用按钮控制网页滚动、以及固定导航栏效果

    实现效果如下: 页面内有三个按钮,分别控制页面向上.向下移动,以及暂停,并设置有导航栏,在滚动到某一位置时显示.且当用户主动控制鼠标滑轮时,滚动效果自动关闭.本页面只是演示如何实现,进行了简单的布局, ...

  6. 微服务从代码到k8s部署应有尽有系列(一)

    从本篇文章开始,我们用一个系列来讲解从需求到上线.从代码到k8s部署.从日志到监控等各个方面的微服务完整实践. 实战项目地址:https://github.com/Mikaelemmmm/go-zer ...

  7. 科普IIS是什么?IIS介绍!

    1.Microsoft IIS 是允许在公共Intranet或Internet上发布信息的Web服务器.Internet Information Server通过运用超文本传输协议(HTTP)传输信息 ...

  8. C# 实例解释面向对象编程中的里氏替换原则

    在面向对象编程中,SOLID 是五个设计原则的首字母缩写,旨在使软件设计更易于理解.灵活和可维护.这些原则是由美国软件工程师和讲师罗伯特·C·马丁(Robert Cecil Martin)提出的许多原 ...

  9. [Java]Thinking in Java 练习2.12

    题目 对HelloDate.java的简单注释文档的示例执行javadoc,然后通过Web浏览器查看结果. 代码 1 //: HW/Ex2_2.java 2 import java.util.*; 3 ...

  10. [杂记]LeTeX模板——ppt

    出处:ShareLeTeX应用模板:https://cn.sharelatex.com/project/5810ad8a07a1ab0f0f8c2ce4 代码如下: 1 % Copyright 200 ...