官术网_书友最值得收藏!

3.1 基本知識(shí)

為了更好地理解PHP 7中zval的實(shí)現(xiàn),本節(jié)先介紹一下相關(guān)的基本知識(shí),包括什么是數(shù)據(jù)類型,PHP 7中都有哪些變量類型,另外會(huì)介紹與變量存儲(chǔ)相關(guān)的堆和棧的基本知識(shí)。

3.1.1 數(shù)據(jù)類型

數(shù)據(jù)類型是一種為了對(duì)數(shù)據(jù)(或數(shù)據(jù)值)進(jìn)行分類而由用戶定義的類型,在數(shù)據(jù)結(jié)構(gòu)中的定義是一個(gè)值集合以及定義在這個(gè)值集合上的一組操作。

基本的數(shù)據(jù)類型有int、double、long、char及各種指針類型。在C語(yǔ)言中,使用變量時(shí),提前定義變量并指定變量類型,而在PHP中變量不需要指定類型,3.2節(jié)將會(huì)講解弱類型的實(shí)現(xiàn)。

C語(yǔ)言的數(shù)據(jù)類型在不同的操作系統(tǒng)中長(zhǎng)度不同,舉個(gè)例子,x86-64系統(tǒng)架構(gòu)下,一個(gè)char類型的數(shù)據(jù)占1個(gè)字節(jié),一個(gè)int類型的數(shù)據(jù)占4個(gè)字節(jié),一個(gè)指針類型的數(shù)據(jù)占8個(gè)字節(jié),一個(gè)long類型的數(shù)據(jù)占8個(gè)字節(jié),可以在gdb下使用sizeof打印驗(yàn)證:

    (gdb) p sizeof(char)
    $1 = 1
    (gdb) p sizeof(int)
    $2 = 4
    (gdb) p sizeof(long)
    $3 = 8
    (gdb) p sizeof(char*)
    $4 = 8
    (gdb) p sizeof(void*)
    $5 = 8

注意

類型所占空間大小與系統(tǒng)架構(gòu)有關(guān),可以使用show architecture來查看:

    (gdb) show architecture
    The target architecture is set automatically (currently i386:x86-64)

也可以使用set architecture i386:x86-64來設(shè)置:

    (gdb) set architecture i386:x86-64
    The target architecture is assumed to be i386:x86-64

3.1.2 結(jié)構(gòu)體與聯(lián)合體

結(jié)構(gòu)體是使用struct定義的結(jié)構(gòu),比如定義一個(gè)如下的結(jié)構(gòu)體:

    struct test{
        char a; //1
        int b;  //4
        long c; //8
        void* d; //8
        int e;  //4
        char* f; //8
    };

在代碼中標(biāo)記了每個(gè)成員的大小,那么結(jié)構(gòu)體的總大小是1+4+8+8+4+8=33嗎?

下面來打印一下:

    (gdb) p sizeof(struct test)
    $1 = 40

為什么是40而不是33呢?這里面涉及結(jié)構(gòu)體對(duì)齊問題,在筆者的機(jī)器上,結(jié)構(gòu)體是按照8字節(jié)對(duì)齊的,那么可以畫出對(duì)齊后占用的字節(jié)數(shù),如圖3-1所示。

圖3-1 結(jié)構(gòu)體

從圖3-1中可看出,雖然char a只占了1字節(jié),int b只占了4字節(jié),但是long c并不是緊跟著b,而是根據(jù)8字節(jié)對(duì)齊后,c和b之間空了3字節(jié)。同樣,char* f和int e之間也空了4字節(jié),因此總大小為40字節(jié)。雖然浪費(fèi)了7字節(jié),但得益于內(nèi)存對(duì)齊,存取速度會(huì)更快。這是結(jié)構(gòu)體對(duì)齊的基礎(chǔ)。

接下來討論一下聯(lián)合體(union),聯(lián)合體的定義跟結(jié)構(gòu)體類似,定義一個(gè)如下的聯(lián)合體:

    union test{
        char a; //1
        int b;  //4
        long c; //8
    };

同樣打印一下這個(gè)聯(lián)合體的大?。?/p>

    (gdb) p sizeof(union test)
    $1 = 8

那么聯(lián)合體是怎樣的一種格式呢?它復(fù)用了同一塊內(nèi)存,如圖3-2所示。

圖3-2 聯(lián)合體

從圖中可以看出,a、b和c共用同一塊內(nèi)存,修改a,也會(huì)影響b和c的值,同時(shí)可以知道聯(lián)合體的大小為其最大成員的大小,比如圖3-2中聯(lián)合體test的大小為其最大的成員long c的大小,也就是8。

這里簡(jiǎn)單學(xué)習(xí)了結(jié)構(gòu)體和聯(lián)合體的區(qū)別,以及結(jié)構(gòu)體的對(duì)齊,接下來再回顧一下程序執(zhí)行時(shí)的堆和棧相關(guān)的基本知識(shí)。

注意

聯(lián)合體是成員變量共享一塊內(nèi)存,可以根據(jù)使用確定含義;而結(jié)構(gòu)體是不共享的,成員變量不共享一塊內(nèi)存。另外,結(jié)構(gòu)體存在對(duì)齊問題。

3.1.3 堆和棧的基本知識(shí)

程序執(zhí)行時(shí)的內(nèi)存布局主要如下:

圖3-3 程序的堆和棧

1)棧區(qū)(stack)——存儲(chǔ)參數(shù)值、局部變量,維護(hù)函數(shù)調(diào)用關(guān)系等。

2)堆區(qū)(heap)——?jiǎng)討B(tài)內(nèi)存區(qū)域,隨時(shí)申請(qǐng)和釋放,程序自身要對(duì)內(nèi)存泄漏負(fù)責(zé)。

3)全局區(qū)(靜態(tài)區(qū))——存儲(chǔ)全局和靜態(tài)變量。

4)字面量區(qū)——常量字符串存儲(chǔ)區(qū)。

5)程序代碼區(qū)——存儲(chǔ)二進(jìn)制代碼。

程序的堆和棧如圖3-3所示。

接下來寫一段C代碼來理解一下各變量分別存在哪個(gè)段區(qū),代碼如下:

    int a=0;     //全局初始化區(qū)
    char *p1;    //全局未初始化區(qū)
    main()
    {
      static int b=0;    //全局(靜態(tài))初始
    化區(qū)
      int c; //棧
      char d[]="abc";    //棧
      char *p2;          //棧
      char *p3 = "hello";    //hello\0在常量區(qū),p3在棧上
      p1 = (char*)malloc(10);
      p2 = (char*)malloc(20); //分配得來的10和20字節(jié)的區(qū)域就在堆區(qū)
      strcpy(p1, "hello");     //hello\0放在常量區(qū),編譯器可能會(huì)將它與p3所指向的"hello"優(yōu)化成
          一個(gè)地方
    }

總體來講,棧上的變量是局部的,隨著局部空間的銷毀而銷毀,由系統(tǒng)負(fù)責(zé)。。

堆上的變量可以提供全局訪問,需要自行處理其生命周期。

這一節(jié)學(xué)習(xí)了數(shù)據(jù)類型、結(jié)構(gòu)體、聯(lián)合體,以及程序運(yùn)行時(shí)的堆和棧信息等基本知識(shí),接下來討論一下PHP中的變量。

主站蜘蛛池模板: 温泉县| 信丰县| 海丰县| 称多县| 上饶市| 乡宁县| 布拖县| 铅山县| 吴江市| 桂阳县| 台南县| 澄江县| 莲花县| 门源| 定陶县| 陕西省| 莒南县| 水富县| 随州市| 刚察县| 布尔津县| 安吉县| 儋州市| 那坡县| 乐至县| 元谋县| 北海市| 周至县| 常德市| 浪卡子县| 黔西县| 襄垣县| 阿拉善盟| 盐山县| 永昌县| 施秉县| 辉县市| 和政县| 昭平县| 呼图壁县| 蓬莱市|