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

grapesegmentfault

所有视频:https://segmentfault.com/a/11...函数

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

引入

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

宏的基础知识

1、宏替换基础知识:

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

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

  1. 每次宏展开的结果会被重复扫描,知道没有任何可展开的宏为止。
  2. 每展开一个宏,都会记住此次展开,在这个宏展开的结果及其后续展开中,再也不对相同的宏作展开。
  3. 带参数的宏,先对参数作展开,除非定义体中包含#或##code

    a. '#'表示将后续标识转化为字符串。
    b. '##'标识将两个标识链接成一个标识符。
    c. 注意参数展开的结果中即便有逗号,也不要视为参数的分隔符。
  4. 若是宏定义中带有参数,而代码中出现一样标识时没有参数,不视为宏。

示例

  1. 首先咱们看一个最简单的替换:视频

    #include <stdio.h>
    
    #define foo(bar) bar
    
    int main()
    {
       printf("%s\n",foo("grape")); 
        return 0;
    }

    结果相信你们一眼就能够看出来,是的输出“grape”,如图所示:
    clipboard.pngblog

  2. 对应于注意事项中的的一项,展开全部的宏,咱们来看这样一个代码:递归

    #include <stdio.h>
    
    #define foo(bar) bar1
    #define bar1 "hello"
    int main()
    {
       printf("%s\n",foo("grape")); 
        return 0;
    }

    结果是什么呢?
    好的,结果和你们想的同样,就是hello,如图所示:ip

    clipboard.png

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

    #define foo foo bar

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

    foo
    //|->foo bar
    //|  |~    |->bar bar foo
    //|  |-> foo bar bar foo (至此展开完毕)

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

  4. 对于#和##的注意,在咱们的平常代码学习中,咱们不多碰见#和##,因此相信你们对此都十分陌生,如今让咱们来看看它究竟有什么做用。见代码:

    #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会输出什么?
    结果如图所示:

    clipboard.png

    而后咱们能够想一下,若是没有#和##会输出?

    #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;
    }

    结果如图所示:

    clipboard.png

    对比二者咱们会发现#和##的做用。即带参数的宏执行时,咱们一般先对参数的宏进行展开,可是,在参数的宏中拥有#或者##的时候,会最后才进行展开。

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

    #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笔记

结尾

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

相关文章
相关标签/搜索