搜尋
首頁後端開發php教程PHP是如何儲存變數的? zval結構體你了解嗎?

PHP 原始碼中的zval

在PHP 中定義一個變數是不需要宣告類型的,一開始給變數$a 一個整數值,後面又可以輕易地將其改變為其他類型。那在 PHP 的源碼中是如何來儲存這個變數 $a 的呢?帶著這個疑問我們一起去看 PHP 的原始碼。

PHP 的原始碼是由 C 編寫的,在 PHP 的原始碼中使用了一個 zval 的結構體來儲存在 PHP 程式碼中建立的變數。我們把 zval 結構體的定義拿出來簡單分析一下。

這是 PHP 在 Github 上的官方倉庫:github.com/php/php-src,本文使用的分支是 PHP-7.4.29。

zval 結構體

在PHP 的原始碼中找到這個檔案:php-src/Zend/zend_types.h,可以看到其中zval 結構體的定義如下,左側是原始碼。原始碼中使用了 PHP 自己定義的型別 zend_uchar 、uint16_t 、uint32_t 等,這些型別會針對不同平台和編譯器會轉為該平台下的 char short int 等。為了便於理解,我將其翻譯為普通類型並展示在了原始碼的右側。同時也把其中的巨集函數 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 結構體中,變數的值就儲存在 zend_value 類型的 value 屬性中。並且透過 u1.v.type 來記錄這個值是什麼類型的,例如 IS_LONG 對應整數,IS_STRING 對應字串類型。

zend_value 聯合體

zend_value 類型也是在php-src/Zend/zend_types.h 中定義的,是一個聯合體,下面是zend_value 聯合體的定義,左側是原始碼。同樣在右側我也做了簡單的翻譯,把 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;

聯合體的一個特點是其佔用的記憶體是其屬性中最大類型對應的長度。其中的 zend_long 就是 long 類型,可以看到 long 類型的 lval 和 double 類型的 dval 所佔用的長度都是 8 個位元組。裡面其他指標類型,也都是 8 個位元組。最後面的結構體屬性 ww 是由兩個 int 型構成,長度相加也是 8 個位元組。因此此聯合體的長度為 8 個位元組。

在我們寫的 PHP 程式碼中,整數和浮點型資料的值會直接存放到 lval 和 dval 中。如果是字串、陣列以及其他類型時會開闢一段空間儲存數據,並將其位址存放在zend_value 中,也就是zval.value 屬性,如:zval.value.zend_long = 9527、zval.value.zend_string = 字元字串位址、zval.value.zend_array = 陣列位址。然後在 zval.u1.v.type 上標記這個 zval.value 是整數、或浮點型、或字串、或其他型別。

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 結構體記憶體佔用

接下來我們分析一下zval 所需佔用的記憶體。

  • value:zend_value 類型 8 個位元組。

  • u1:

  • u1.v.type:unsigned char 1 個位元組,u1.v.type_flags :unsigned char 1 個位元組,u1.v.u:聯合體中只有一個unsigned short 的extra 屬性2 個位元組,因此u1.v 的結構體總共是4 個位元組。

  • u1.type_info:unsigned int 4 個位元組。

  • 因此 u1 這個聯合體的長度取最長的屬性的長度:4 個位元組。

  • u2:也是一個聯合體,裡面都是 int 型的屬性,因此長度是 4 個位元組。

  • 所以 zval 總共佔用的記憶體是 8 4 4 = 16 個位元組。

也就是說當我們在寫PHP 程式碼時,如果創建了一個整數的變量,那麼實際上它在運行中會佔用16 個位元組的內存,內存開銷至少是C 語言的兩倍。當然這兩倍的開銷也帶來了 PHP 處理變數的彈性。

推薦學習:《PHP影片教學

以上是PHP是如何儲存變數的? zval結構體你了解嗎?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:learnku。如有侵權,請聯絡admin@php.cn刪除
繼續使用PHP:耐力的原因繼續使用PHP:耐力的原因Apr 19, 2025 am 12:23 AM

PHP仍然流行的原因是其易用性、靈活性和強大的生態系統。 1)易用性和簡單語法使其成為初學者的首選。 2)與web開發緊密結合,處理HTTP請求和數據庫交互出色。 3)龐大的生態系統提供了豐富的工具和庫。 4)活躍的社區和開源性質使其適應新需求和技術趨勢。

PHP和Python:探索他們的相似性和差異PHP和Python:探索他們的相似性和差異Apr 19, 2025 am 12:21 AM

PHP和Python都是高層次的編程語言,廣泛應用於Web開發、數據處理和自動化任務。 1.PHP常用於構建動態網站和內容管理系統,而Python常用於構建Web框架和數據科學。 2.PHP使用echo輸出內容,Python使用print。 3.兩者都支持面向對象編程,但語法和關鍵字不同。 4.PHP支持弱類型轉換,Python則更嚴格。 5.PHP性能優化包括使用OPcache和異步編程,Python則使用cProfile和異步編程。

PHP和Python:解釋了不同的範例PHP和Python:解釋了不同的範例Apr 18, 2025 am 12:26 AM

PHP主要是過程式編程,但也支持面向對象編程(OOP);Python支持多種範式,包括OOP、函數式和過程式編程。 PHP適合web開發,Python適用於多種應用,如數據分析和機器學習。

PHP和Python:深入了解他們的歷史PHP和Python:深入了解他們的歷史Apr 18, 2025 am 12:25 AM

PHP起源於1994年,由RasmusLerdorf開發,最初用於跟踪網站訪問者,逐漸演變為服務器端腳本語言,廣泛應用於網頁開發。 Python由GuidovanRossum於1980年代末開發,1991年首次發布,強調代碼可讀性和簡潔性,適用於科學計算、數據分析等領域。

在PHP和Python之間進行選擇:指南在PHP和Python之間進行選擇:指南Apr 18, 2025 am 12:24 AM

PHP適合網頁開發和快速原型開發,Python適用於數據科學和機器學習。 1.PHP用於動態網頁開發,語法簡單,適合快速開發。 2.Python語法簡潔,適用於多領域,庫生態系統強大。

PHP和框架:現代化語言PHP和框架:現代化語言Apr 18, 2025 am 12:14 AM

PHP在現代化進程中仍然重要,因為它支持大量網站和應用,並通過框架適應開發需求。 1.PHP7提升了性能並引入了新功能。 2.現代框架如Laravel、Symfony和CodeIgniter簡化開發,提高代碼質量。 3.性能優化和最佳實踐進一步提升應用效率。

PHP的影響:網絡開發及以後PHP的影響:網絡開發及以後Apr 18, 2025 am 12:10 AM

PHPhassignificantlyimpactedwebdevelopmentandextendsbeyondit.1)ItpowersmajorplatformslikeWordPressandexcelsindatabaseinteractions.2)PHP'sadaptabilityallowsittoscaleforlargeapplicationsusingframeworkslikeLaravel.3)Beyondweb,PHPisusedincommand-linescrip

PHP類型提示如何起作用,包括標量類型,返回類型,聯合類型和無效類型?PHP類型提示如何起作用,包括標量類型,返回類型,聯合類型和無效類型?Apr 17, 2025 am 12:25 AM

PHP類型提示提升代碼質量和可讀性。 1)標量類型提示:自PHP7.0起,允許在函數參數中指定基本數據類型,如int、float等。 2)返回類型提示:確保函數返回值類型的一致性。 3)聯合類型提示:自PHP8.0起,允許在函數參數或返回值中指定多個類型。 4)可空類型提示:允許包含null值,處理可能返回空值的函數。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱工具

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。