grapesegmentfault
所有视频:https://segmentfault.com/a/11...函数
原视频地址:http://replay.xesv5.com/ll/24...学习
咱们知道宏定义的优势有方便程序的修改,提升程序运行效率等等。而且在咱们平常的代码学习中,咱们会碰到过不少不少的宏定义。针对这些宏定义,咱们一般都是秉承着“宏便是替换”的“法则”来进行分析。然而,对于一些简单的宏定义来讲,咱们直接进行替换便可完美的解决问题,可是针对于一些复杂的宏定义来讲,咱们会发现,替换也是有些门道的。那么,咱们今天就来探索一下宏定义的神奇吧。ui
#define 宏名 字符串
#define 宏名(形参列表) 字符串
容许宏带有参数,在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数spa
带参数的宏,先对参数作展开,除非定义体中包含#或##code
a. '#'表示将后续标识转化为字符串。 b. '##'标识将两个标识链接成一个标识符。 c. 注意参数展开的结果中即便有逗号,也不要视为参数的分隔符。
首先咱们看一个最简单的替换:视频
#include <stdio.h> #define foo(bar) bar int main() { printf("%s\n",foo("grape")); return 0; }
结果相信你们一眼就能够看出来,是的输出“grape”,如图所示:blog
对应于注意事项中的的一项,展开全部的宏,咱们来看这样一个代码:递归
#include <stdio.h> #define foo(bar) bar1 #define bar1 "hello" int main() { printf("%s\n",foo("grape")); return 0; }
结果是什么呢?
好的,结果和你们想的同样,就是hello,如图所示:ip
继续,对于第二个注意事项,首先咱们分析一下这个事项是为何。相信你们都知道递归,假若一个递归没有结束条件会怎么样,结果确定是无限的执行下去,若是,咱们的宏定义也会出现这个状况,那。。。读者自行脑补吧。基于这个场景咱们来看看这第二条规则,咱们看一下这种状况,固然为了简单,这段代码是不可执行的:
#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会输出什么?
结果如图所示:
而后咱们能够想一下,若是没有#和##会输出?
#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; }
结果如图所示:
对比二者咱们会发现#和##的做用。即带参数的宏执行时,咱们一般先对参数的宏进行展开,可是,在参数的宏中拥有#或者##的时候,会最后才进行展开。
第四点注意事项,就会很容易理解,举个例子,声明一个有入参的函数,若是你只去调用函数名会出现什么问题?固然,还有另一种状况,例如:
#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源码学习】2019-03-11 PHP内存管理3笔记。
在咱们的工做或者学习中,会出现不少复杂的宏替换,只要咱们认定“宏便是替换”以及记住以上注意事项,那么一切复杂宏替换都是纸老虎。