您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“PHP是怎么存儲(chǔ)變量的”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“PHP是怎么存儲(chǔ)變量的”吧!
在 PHP 中定義一個(gè)變量是不需要聲明類型的,一開始給變量 $a 賦予一個(gè)整型值,后面又可以輕而易舉地將其改變?yōu)槠渌愋?。那?PHP 的源碼中是如何來存儲(chǔ)這個(gè)變量 $a 的呢?帶著這個(gè)疑問我們一起去看一看 PHP 的源碼。
PHP 的源碼是由 C 編寫的,在 PHP 的源碼中使用了一個(gè) zval 的結(jié)構(gòu)體來存儲(chǔ)在 PHP 代碼中創(chuàng)建的變量。我們把 zval 結(jié)構(gòu)體的定義拿出來簡(jiǎn)單分析一下。
這是 PHP 在 Github 上的官方倉(cāng)庫(kù):github.com/php/php-src,本文使用的分支是 PHP-7.4.29。
zval 結(jié)構(gòu)體
在 PHP 的源碼中找到這個(gè)文件:php-src/Zend/zend_types.h,可以看到其中 zval 結(jié)構(gòu)體的定義如下,左側(cè)是源碼。源碼中使用了 PHP 自己定義的類型 zend_uchar 、uint16_t 、uint32_t 等,這些類型會(huì)針對(duì)不同平臺(tái)和編譯器會(huì)轉(zhuǎn)為該平臺(tái)下的 char short int 等。為了便于理解,我將其翻譯為普通類型并展示在了源碼的右側(cè)。同時(shí)還把其中的宏函數(shù) ZEND_ENDIAN_LOHI_3() 也展開了。
typedef struct _zval_struct zval; ... 《源代碼》 《翻譯后》 ------------------------------------------------------------------------------------------- struct _zval_struct { | struct _zval_struct { zend_value value; | zend_value value; union { | union { struct { | struct { ZEND_ENDIAN_LOHI_3( | unsigned char type; zend_uchar type, | unsigned char type_flags; zend_uchar type_flags, | union { union { | unsigned short extra; uint16_t extra; | } u; } u | } v; ) | unsigned int type_info; } v; | } u1; uint32_t type_info; | union { } u1; | unsigned int next; union { | unsigned int cache_slot; uint32_t next; | unsigned int opline_num; uint32_t cache_slot; | unsigned int lineno; uint32_t opline_num; | unsigned int num_args; uint32_t lineno; | unsigned int fe_pos; uint32_t num_args; | unsigned int fe_iter_idx; uint32_t fe_pos; | unsigned int access_flags; uint32_t fe_iter_idx; | unsigned int property_guard; uint32_t access_flags; | unsigned int constant_flags; uint32_t property_guard; | unsigned int extra; uint32_t constant_flags; | } u2; uint32_t extra; | }; } u2; | }; |
在 zval 結(jié)構(gòu)體中,變量的值就存儲(chǔ)在 zend_value 類型的 value 屬性中。并通過 u1.v.type 來記錄這個(gè)值是什么類型的,比如 IS_LONG 對(duì)應(yīng)整型,IS_STRING 對(duì)應(yīng)字符串類型。
zend_value 聯(lián)合體
zend_value 類型也是在 php-src/Zend/zend_types.h 中定義的,是一個(gè)聯(lián)合體,下面是 zend_value 聯(lián)合體的定義,左側(cè)是源碼。同樣在右側(cè)我也做了簡(jiǎn)單的翻譯,把 zend_long uint32_t 翻譯為普通類型便于查看。
《源代碼》 《翻譯后》 ------------------------------------------------------------------------------------ typedef union _zend_value { | typedef union _zend_value { zend_long lval; /* long value */ | long lval; double dval; /* double value */ | double dval; zend_refcounted *counted; | zend_refcounted *counted; zend_string *str; | zend_string *str; zend_array *arr; | zend_array *arr; zend_object *obj; | zend_object *obj; zend_resource *res; | zend_resource *res; zend_reference *ref; | zend_reference *ref; zend_ast_ref *ast; | zend_ast_ref *ast; zval *zv; | zval *zv; void *ptr; | void *ptr; zend_class_entry *ce; | zend_class_entry *ce; zend_function *func; | zend_function *func; struct { | struct { uint32_t w1; | unsigned int w1; uint32_t w2; | unsigned int w2; } ww; | } ww; } zend_value; | } zend_value;
聯(lián)合體的一個(gè)特點(diǎn)是其占用的內(nèi)存是其屬性中最大類型對(duì)應(yīng)的長(zhǎng)度。其中的 zend_long 就是 long 類型,可以看到 long 類型的 lval 和 double 類型的 dval 占用的長(zhǎng)度都是 8 個(gè)字節(jié)。里面其他指針類型,也均為 8 個(gè)字節(jié)。最后面的結(jié)構(gòu)體屬性 ww 是由兩個(gè) int 型構(gòu)成,長(zhǎng)度相加也是 8 個(gè)字節(jié)。因此此聯(lián)合體的長(zhǎng)度為 8 個(gè)字節(jié)。
在我們寫的 PHP 代碼中,整型和浮點(diǎn)型數(shù)據(jù)的值會(huì)直接存放到 lval 和 dval 中。如果是字符串、數(shù)組以及其他類型時(shí)會(huì)開辟一段空間存儲(chǔ)數(shù)據(jù),并將其地址存放在 zend_value 中,也就是 zval.value 屬性,如:zval.value.zend_long = 9527、zval.value.zend_string = 字符串地址 、zval.value.zend_array = 數(shù)組地址。然后在 zval.u1.v.type 上標(biāo)記這個(gè) zval.value 是整型、或浮點(diǎn)型、或字符串、或其他類型。
zval.u1.v.type 類型定義也是在 php-src/Zend/zend_types.h 文件中,全部的定義如下:
/* regular data types */ #define IS_UNDEF 0 #define IS_NULL 1 #define IS_FALSE 2 #define IS_TRUE 3 #define IS_LONG 4 #define IS_DOUBLE 5 #define IS_STRING 6 #define IS_ARRAY 7 #define IS_OBJECT 8 #define IS_RESOURCE 9 #define IS_REFERENCE 10 /* constant expressions */ #define IS_CONSTANT_AST 11 /* internal types */ #define IS_INDIRECT 13 #define IS_PTR 14 #define IS_ALIAS_PTR 15 #define _IS_ERROR 15 /* fake types used only for type hinting (Z_TYPE(zv) can not use them) */ #define _IS_BOOL 16 #define IS_CALLABLE 17 #define IS_ITERABLE 18 #define IS_VOID 19 #define _IS_NUMBER 20
zval 結(jié)構(gòu)體內(nèi)存占用
接下來我們分析一下 zval 所需占用的內(nèi)存。
value:zend_value 類型 8 個(gè)字節(jié)。
u1:
u1.v.type:unsigned char 1 個(gè)字節(jié),u1.v.type_flags:unsigned char 1 個(gè)字節(jié),u1.v.u:聯(lián)合體中只有一個(gè) unsigned short 的 extra 屬性 2 個(gè)字節(jié),因此 u1.v 的結(jié)構(gòu)體總共是 4 個(gè)字節(jié)。
u1.type_info:unsigned int 4 個(gè)字節(jié)。
因此 u1 這個(gè)聯(lián)合體的長(zhǎng)度取最長(zhǎng)的屬性的長(zhǎng)度:4 個(gè)字節(jié)。
u2:也是一個(gè)聯(lián)合體,里面都是 int 型的屬性,因此長(zhǎng)度是 4 個(gè)字節(jié)。
所以 zval 總共占用的內(nèi)存是 8 + 4 + 4 = 16 個(gè)字節(jié)。
也就是說當(dāng)我們?cè)趯?PHP 代碼時(shí),如果創(chuàng)建了一個(gè)整型的變量,那么實(shí)際上它在運(yùn)行中會(huì)占用 16 個(gè)字節(jié)的內(nèi)存,內(nèi)存開銷至少是 C 語(yǔ)言的兩倍。當(dāng)然這兩倍的開銷也帶來了 PHP 處理變量的靈活性。
到此,相信大家對(duì)“PHP是怎么存儲(chǔ)變量的”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。