淺談php變數的實現-PHP

weixin_34402408發表於2018-05-20

php  @amazeUI  2017-02-04 08:34:48

7027304-b0a7097155500ae5.jpg

1.php是如何執行的

php作為一個指令碼語言,但不是靠直譯器來解釋語言。php程式碼首先經過zend編譯器,將php程式碼編譯成opcode,再由虛擬的一個zend虛擬機器來執行這段opcode,這種執行模式與java有些類似,java是先編譯成 .class檔案再由虛擬機器來執行,java本身這個語言不是跨平臺的,而是這個虛擬機器是跨平臺的,java程式執行完畢後,class檔案會儲存下來,下次執行直接執行,與php不同,php轉成的opcode當程式執行結束後opcode會被清除不會被保留,下次再執行會再次生成opcode。

2.php是c語言實現的,而c語言是強型別語言,php是弱型別語言,這是如何實現的zend.h

php無需宣告一個變數的型別,就可以賦值,底層是如何描述這個變數的呢,php的變數在底層是以一個基本的結構體描述一個變數的。

這個是php變數在記憶體中的儲存結構(在php原始碼Zend/zend.h裡)

struct _zval_struct {

/* Variable information */

zvalue_value value;     /* value */->這個是一個聯合體,儲存著這個變數的值和其他資訊

zend_uint refcount__gc;->這裡最好理解成這個結構體一共有幾個變數在使用

zend_uchar type;    /* active type */->這裡儲存這個值的變數的基礎型別(一共八種)

zend_uchar is_ref__gc;->這裡表示這個變數是否為引用的值的(0為否,1為是)

};

這個就是上面所說的聯合體

typedef union _zvalue_value {

long lval;                  /* long value */

double dval;                /* double value */

struct {

char *val;

int len;

} str;->一個字串型別,裡面包含了一個字串長度

HashTable *ht;              /* hash table value */->這帶包一個雜湊表指標

zend_object_value obj;->這代表物件

} zvalue_value;

如果仔細觀察會發現這個聯合只有五種資料結構,並沒有php的八種資料結構(結構體裡也是八種),和php的八種資料結構差了三個,null,bool,resource。

可以確定的是所有變數都是由這個結構體來實現的,null在底層可以沒有這個型別,在結構體裡的type直接設定為IS_NULL,zvalue_value就可以不必設定了,布林型別的在type裡為is_bool,在聯合體中直接設定為0/1就可以了,資源型別在type裡設定為資源,然後在value裡的表示為一個介面的編號。

那$a=1;這一個動作發生了什麼呢,執行到這一句,在記憶體中會多出一個結構體和一個聯合體,在全域性符號表(也就是雜湊表)中會多出一條記錄,記錄這個變數的名字a,和這個變數的值的記憶體地址,也就是那個結構體的記憶體地址。

那$b=$a;這一傳值賦值發生了什麼。執行到這一句,並沒有再多出一個結構體,只是全域性符號表裡再多出一條記錄也是指向$a這個結構體的,這個結構體的refcount__gc變為2,表示有兩個變數名指向了這個結構體。如果隨便再給$a或者$b賦一個其他的值,結構體會分裂成兩個,比如說$a=2;首先會看這個結構體的is_ref__gc是否等於0,如果是,則這個時候會多出一個結構體,雜湊表裡的$a的地址也會指向值為2的結構體。這種特性叫做寫時複製(簡寫為cow,copy on write),很多種語言都有這種特性,就是為了省記憶體。如果不是傳值賦值而是引用賦值的話,is_ref_gc等於1,公用一個結構體。

鳥哥的部落格裡有更深入的解釋

相關文章