资讯专栏INFORMATION COLUMN

【PHP源码学习】2019-03-26 宏定义笔记

SKYZACK / 1906人阅读

摘要:并且在我们日常的代码学习中,我们会碰到过很多很多的宏定义。如果宏定义中带有参数,而代码中出现同样标识时没有参数,不视为宏。具体的解析见源码学习内存管理笔记。

grape

全部视频:https://segmentfault.com/a/11...

原视频地址:http://replay.xesv5.com/ll/24...

引入

我们知道宏定义的优点有方便程序的修改,提高程序运行效率等等。并且在我们日常的代码学习中,我们会碰到过很多很多的宏定义。针对这些宏定义,我们通常都是秉承着“宏即是替换”的“法则”来进行分析。然而,对于一些简单的宏定义来说,我们直接进行替换即可完美的解决问题,但是针对于一些复杂的宏定义来说,我们会发现,替换也是有些门道的。那么,我们今天就来探索一下宏定义的神奇吧。

宏的基础知识 一、宏替换基础知识:

#define 宏名 字符串
#define 宏名(形参列表) 字符串
允许宏带有参数,在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数

二、C宏展开的几个注意事项:

每次宏展开的结果会被重复扫描,知道没有任何可展开的宏为止。

每展开一个宏,都会记住这次展开,在这个宏展开的结果及其后续展开中,不再对相同的宏做展开。

带参数的宏,先对参数做展开,除非定义体中包含#或##

</>复制代码

  1. a. "#"表示将后续标识转化为字符串。
  2. b. "##"标识将两个标识连接成一个标识符。
  3. c. 注意参数展开的结果中即使有逗号,也不要视为参数的分隔符。

如果宏定义中带有参数,而代码中出现同样标识时没有参数,不视为宏。

示例

首先我们看一个最简单的替换:

</>复制代码

  1. #include
  2. #define foo(bar) bar
  3. int main()
  4. {
  5. printf("%s
  6. ",foo("grape"));
  7. return 0;
  8. }

结果相信大家一眼就可以看出来,是的输出“grape”,如图所示:

对应于注意事项中的的一项,展开所有的宏,我们来看这样一个代码:

</>复制代码

  1. #include
  2. #define foo(bar) bar1
  3. #define bar1 "hello"
  4. int main()
  5. {
  6. printf("%s
  7. ",foo("grape"));
  8. return 0;
  9. }

结果是什么呢?
好的,结果和大家想的一样,就是hello,如图所示:

继续,对于第二个注意事项,首先我们分析一下这个事项是为什么。相信大家都知道递归,倘若一个递归没有结束条件会怎么样,结果肯定是无限的执行下去,如果,我们的宏定义也会出现这个情况,那。。。读者自行脑补吧。基于这个场景我们来看看这第二条规则,我们看一下这种情况,当然为了简单,这段代码是不可执行的:

</>复制代码

  1. #define foo foo bar

我们来看这个foo的定义,如果我们不知道这项规则,这段代码被我们来解析,按照替换来讲,我们是不是会认为是"... bar bar foo ..."这样子?然而真实的情况是这样子的:

</>复制代码

  1. foo
  2. //|->foo bar
  3. //| |~ |->bar bar foo
  4. //| |-> foo bar bar foo (至此展开完毕)

所以,同一个宏定义是不可循环展开的。

对于#和##的注意,在我们的日常代码学习中,我们很少遇见#和##,所以相信大家对此都十分陌生,现在让我们来看看它究竟有什么作用。见代码:

</>复制代码

  1. #include
  2. #define f(a,b) a##b
  3. #define g(a) #a
  4. #define h(a) g(a)
  5. int main()
  6. {
  7. printf("%s
  8. ",h(f(1,2))); //result1
  9. printf("%s
  10. ",g(f(1,2))); //result2
  11. return 0;
  12. }

大家可以先看一下代码,考虑一下result1和result2会输出什么?
结果如图所示:

然后我们可以想一下,如果没有#和##会输出?

</>复制代码

  1. #include
  2. #define f(a,b) b
  3. #define g(a) a
  4. #define h(a) g(a)
  5. int main()
  6. {
  7. printf("%d
  8. ",f(1,2));
  9. printf("%d
  10. ",h(f(1,2)));
  11. printf("%d
  12. ",g(f(1,2)));
  13. return 0;
  14. }

结果如图所示:

对比两者我们会发现#和##的作用。即带参数的宏执行时,我们通常先对参数的宏进行展开,但是,在参数的宏中拥有#或者##的时候,会最后才进行展开。

第四点注意事项,就会很容易理解,举个例子,声明一个有入参的函数,如果你只去调用函数名会出现什么问题?当然,还有另外一种情况,例如:

</>复制代码

  1. #define _BIN_DATA_SIZE(num, size, elements, pages, x, y) size,
  2. static const uint32_t bin_data_size[] = {
  3. ZEND_MM_BINS_INFO(_BIN_DATA_SIZE, x, y)
  4. };
  5. #define ZEND_MM_BINS_INFO(_, x, y)
  6. _( 0, 8, 512, 1, x, y)
  7. _( 1, 16, 256, 1, x, y)
  8. _( 2, 24, 170, 1, x, y)
  9. _( 3, 32, 128, 1, x, y)
  10. _( 4, 40, 102, 1, x, y)
  11. _( 5, 48, 85, 1, x, y)
  12. _( 6, 56, 73, 1, x, y)
  13. _( 7, 64, 64, 1, x, y)
  14. _( 8, 80, 51, 1, x, y)
  15. _( 9, 96, 42, 1, x, y)
  16. _(10, 112, 36, 1, x, y)
  17. _(11, 128, 32, 1, x, y)
  18. _(12, 160, 25, 1, x, y)
  19. _(13, 192, 21, 1, x, y)
  20. _(14, 224, 18, 1, x, y)
  21. _(15, 256, 16, 1, x, y)
  22. _(16, 320, 64, 5, x, y)
  23. _(17, 384, 32, 3, x, y)
  24. _(18, 448, 9, 1, x, y)
  25. _(19, 512, 8, 1, x, y)
  26. _(20, 640, 32, 5, x, y)
  27. _(21, 768, 16, 3, x, y)
  28. _(22, 896, 9, 2, x, y)
  29. _(23, 1024, 8, 2, x, y)
  30. _(24, 1280, 16, 5, x, y)
  31. _(25, 1536, 8, 3, x, y)
  32. _(26, 1792, 16, 7, x, y)
  33. _(27, 2048, 8, 4, x, y)
  34. _(28, 2560, 8, 5, x, y)
  35. _(29, 3072, 4, 3, x, y)

我们在第一次看到_BIN_DATA_SIZE只认为是一个形量传入到函数中,没有做宏替换,在_替换之后会被扫描到重新做替换。具体的解析见【PHP源码学习】2019-03-11 PHP内存管理3笔记。

结尾

在我们的工作或者学习中,会出现很多复杂的宏替换,只要我们认定“宏即是替换”以及记住以上注意事项,那么一切复杂宏替换都是纸老虎。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/31519.html

相关文章

  • 【LNMPR源码学习笔记汇总

    摘要:此文用于汇总跟随陈雷老师及团队的视频,学习源码过程中的思考整理与心得体会,此文会不断更新视频传送门每日学习记录使用录像设备记录每天的学习源码学习源码学习内存管理笔记源码学习内存管理笔记源码学习内存管理笔记源码学习基本变量笔记 此文用于汇总跟随陈雷老师及团队的视频,学习源码过程中的思考、整理与心得体会,此文会不断更新 视频传送门:【每日学习记录】使用录像设备记录每天的学习 PHP7...

    Barrior 评论0 收藏0
  • PHP源码学习2019-03-11 PHP内存管理3笔记

    摘要:那么问题来了,为什么要把如此简单的一个数组初始化问题复杂化这样写代码真的真的不会被别人锤吗其实源码中还有其他相关部分我们可以看到,源码中提供了三个类似的宏替换结构。 baiyan 全部视频:https://segmentfault.com/a/11... 源视频地址:http://replay.xesv5.com/ll/24... 复习 PHP内存分配流程 showImg(https:...

    wangjuntytl 评论0 收藏0
  • PHP源码学习2019-03-18 复习前面的内容

    摘要:调用函数时,它将用户释放的内存块连接到空闲链上。这个联合体共占用字节。是数字,且顺序递增位置固定,如访问是的元素,即,就直接访问数组的第个位置即可即,这样就不需要前面的索引数组。 baiyan 全部视频:https://segmentfault.com/a/11... 原视频地址:http://replay.xesv5.com/ll/24... 本笔记中部分图片截自视频中的片段,图片版...

    lindroid 评论0 收藏0

发表评论

0条评论

SKYZACK

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<