内容简介:grape全部视频:原视频地址:
grape
全部视频: https://segmentfault.com/a/11...
原视频地址: http://replay.xesv5.com/ll/24...
引入
我们知道宏定义的优点有方便程序的修改,提高程序运行效率等等。并且在我们日常的代码学习中,我们会碰到过很多很多的宏定义。针对这些宏定义,我们通常都是秉承着“宏即是替换”的“法则”来进行分析。然而,对于一些简单的宏定义来说,我们直接进行替换即可完美的解决问题,但是针对于一些复杂的宏定义来说,我们会发现,替换也是有些门道的。那么,我们今天就来探索一下宏定义的神奇吧。
宏的基础知识
一、宏替换基础知识:
#define 宏名 字符串
#define 宏名(形参列表) 字符串
允许宏带有参数,在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数
二、C宏展开的几个注意事项:
- 每次宏展开的结果会被重复扫描,知道没有任何可展开的宏为止。
- 每展开一个宏,都会记住这次展开,在这个宏展开的结果及其后续展开中,不再对相同的宏做展开。
-
带参数的宏,先对参数做展开,除非定义体中包含#或##
a. '#'表示将后续标识转化为字符串。 b. '##'标识将两个标识连接成一个标识符。 c. 注意参数展开的结果中即使有逗号,也不要视为参数的分隔符。
- 如果宏定义中带有参数,而代码中出现同样标识时没有参数,不视为宏。
示例
-
首先我们看一个最简单的替换:
#include <stdio.h> #define foo(bar) bar int main() { printf("%s\n",foo("grape")); return 0; }结果相信大家一眼就可以看出来,是的输出“grape”,如图1所示:
图1
-
对应于注意事项中的的一项,展开所有的宏,我们来看这样一个代码:
#include <stdio.h> #define foo(bar) bar1 #define bar1 "hello" int main() { printf("%s\n",foo("grape")); return 0; }结果是什么呢?
好的,结果和大家想的一样,就是hello,如图2所示:
图2
-
继续,对于第二个注意事项,首先我们分析一下这个事项是为什么。相信大家都知道递归,倘若一个递归没有结束条件会怎么样,结果肯定是无限的执行下去,如果,我们的宏定义也会出现这个情况,那。。。读者自行脑补吧。基于这个场景我们来看看这第二条规则,我们看一下这种情况,当然为了简单,这段代码是不可执行的:
#define foo foo bar
我们来看这个foo的定义,如果我们不知道这项规则,这段代码被我们来解析,按照替换来讲,我们是不是会认为是"... bar bar foo ..."这样子?然而真实的情况是这样子的:
foo //|->foo bar //| |~ |->bar bar foo //| |-> foo bar bar foo (至此展开完毕)
所以,同一个宏定义是不可循环展开的。
-
对于#和##的注意,在我们的日常代码学习中,我们很少遇见#和##,所以相信大家对此都十分陌生,现在让我们来看看它究竟有什么作用。见代码:
#include <stdio.h> #define f(a,b) a##b #define g(a) #a #define h(a) g(a) int main() { printf("%s\n",h(f(1,2))); //result1 printf("%s\n",g(f(1,2))); //result2 return 0; }大家可以先看一下代码,考虑一下result1和result2会输出什么?
结果如图3所示:
图3
然后我们可以想一下,如果没有#和##会输出?
#include <stdio.h> #define f(a,b) b #define g(a) a #define h(a) g(a) int main() { printf("%d\n",f(1,2)); printf("%d\n",h(f(1,2))); printf("%d\n",g(f(1,2))); return 0; }结果如图4所示:
图4
对比两者我们会发现#和##的作用。 即带参数的宏执行时,我们通常先对参数的宏进行展开,但是,在参数的宏中拥有#或者##的时候,会最后才进行展开。
-
第四点注意事项,就会很容易理解,举个例子,声明一个有入参的函数,如果你只去调用函数名会出现什么问题?当然,还有另外一种情况,例如:
#define _BIN_DATA_SIZE(num, size, elements, pages, x, y) size, static const uint32_t bin_data_size[] = { ZEND_MM_BINS_INFO(_BIN_DATA_SIZE, x, y) }; #define ZEND_MM_BINS_INFO(_, x, y) \ _( 0, 8, 512, 1, x, y) \ _( 1, 16, 256, 1, x, y) \ _( 2, 24, 170, 1, x, y) \ _( 3, 32, 128, 1, x, y) \ _( 4, 40, 102, 1, x, y) \ _( 5, 48, 85, 1, x, y) \ _( 6, 56, 73, 1, x, y) \ _( 7, 64, 64, 1, x, y) \ _( 8, 80, 51, 1, x, y) \ _( 9, 96, 42, 1, x, y) \ _(10, 112, 36, 1, x, y) \ _(11, 128, 32, 1, x, y) \ _(12, 160, 25, 1, x, y) \ _(13, 192, 21, 1, x, y) \ _(14, 224, 18, 1, x, y) \ _(15, 256, 16, 1, x, y) \ _(16, 320, 64, 5, x, y) \ _(17, 384, 32, 3, x, y) \ _(18, 448, 9, 1, x, y) \ _(19, 512, 8, 1, x, y) \ _(20, 640, 32, 5, x, y) \ _(21, 768, 16, 3, x, y) \ _(22, 896, 9, 2, x, y) \ _(23, 1024, 8, 2, x, y) \ _(24, 1280, 16, 5, x, y) \ _(25, 1536, 8, 3, x, y) \ _(26, 1792, 16, 7, x, y) \ _(27, 2048, 8, 4, x, y) \ _(28, 2560, 8, 5, x, y) \ _(29, 3072, 4, 3, x, y)我么在第一次看到_BIN_DATA_SIZE只认为是一个形量传入到函数中,没有做宏替换,在_替换之后会被扫描到重新做替换。
结尾
在我们的工作或者学习中,会出现很多复杂的宏替换,只要我们认定“宏即是替换”以及记住以上注意事项,那么一切复杂宏替换都是纸老虎。
以上所述就是小编给大家介绍的《【PHP源码学习】宏定义》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 自定义Istio Mixer Adapter示例教程(附源码)
- Mybatis源码分析(七)自定义缓存、分页的实现
- spring5 源码深度解析— IOC 之 自定义标签解析
- JVM 源码分析之自定义类加载器如何拉长 YGC
- LFS 8.3 发布,从源码构建自定义 Linux 系统步骤说明
- LFS 8.4 发布,从源码构建自定义 Linux 系统步骤说明
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Haskell School of Music
Paul Hudak、Donya Quick / Cambridge University Press / 2018-10-4 / GBP 42.99
This book teaches functional programming through creative applications in music and sound synthesis. Readers will learn the Haskell programming language and explore numerous ways to create music and d......一起来看看 《The Haskell School of Music》 这本书的介绍吧!