【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筆記

結尾

在咱們的工做或者學習中,會出現不少複雜的宏替換,只要咱們認定「宏便是替換」以及記住以上注意事項,那麼一切複雜宏替換都是紙老虎。

相關文章
相關標籤/搜索