zval介紹 foreach問題 PHP

2021-09-27 23:41:33 字數 3462 閱讀 5744

先看如下**

<?php

$arr = [1, 2, 3];

foreach ($arr as &$item)

echo "\n\n";

foreach ($arr as $item)

預期結果應該是

int(1)

int(2)

int(3)

int(1)

int(2)

int(3)

[finished in 0.1s]

執行結果卻是

int(1)

int(2)

int(3)

int(1)

int(2)

int(2)

[finished in 0.1s]

**改為

<?php

$arr = [1, 2, 3];

foreach ($arr as &$item)

unset($item);

echo "\n\n";

foreach ($arr as $item)

結果就正常了

說原因之前,先了解下zval的大致結構:

「zval結構體中有四個字段,其含義分別為:

屬性名 含義 預設值

refcount__gc 表示引用計數 1

is_ref__gc 表示是否為引用 0

value 儲存變數的值

type 變數具體的型別」

摘錄來自: reeze xia. 「tipi: 深入理解php核心」。

利用xdebug擴充套件,可以看到refcount和is_ref的情況:

<?php

$a = "hello world";

xdebug_debug_zval('a');

然後在php7下執行:

a: (refcount=2, is_ref=0)='hello world'
在php5.5下執行:(我沒試過,因為環境都是php7)

a: (refcount=1, is_ref=0)='hello world'
為什麼7的refcount 為2呢?這裡是php7的字面量也計算了一次次數

<?php

$a = "hello world";

$b = $a . $a;

xdebug_debug_zval('a');

xdebug_debug_zval('b');

結果為:

a: (refcount=2, is_ref=0)='hello world'

b: (refcount=1, is_ref=0)='hello worldhello world'

加上引用符號的時候:

<?php

$a = "hello world";

$b = &$a;

xdebug_debug_zval('a');

xdebug_debug_zval('b');

結果為:

a: (refcount=2, is_ref=1)='hello world'

b: (refcount=2, is_ref=1)='hello world'

foreach 內部的實現類似於:

foreach具體實現

foreach ($arr as $k => $item)

$item = $arr[$k];

foreach ($arr as $k => &$item)

$item = &$arr[$k];

第一次foreach結束的時候, item並沒有釋放掉,item為arr[k]的引用

所以在第二次foreach的時候,item始終為陣列最後乙個元素的引用,迴圈的每次,都給陣列最後一項進行了賦值操作

所以在倒數第二次的迴圈的時候,給最後一項賦值了倒數第二項的值。進行最後一次迴圈的時候,值為倒數第二項的值,最後一項的值已經丟掉了

在第二次進行迴圈的時候,給item進行一次unset操作,因為item是對arr[k]的引用,檢視zval如下

item:(refcount=2, is_ref=1)int 1

item:(refcount=2, is_ref=1)int 2

item:(refcount=2, is_ref=1)int 3

進行unset操作只是對應的變數的refcount減1,並不會刪除變數內容

所以第二次的結果就正常了

php的引用計數是對於記憶體進行的優化,給變數賦值操作的話,不進行拼接等操作,只是引用計數增加,並不會增加記憶體空間的占用

對於只是記憶體操作的話,比如拼裝where條件等(php7.1之後,支援型別限制)

function test_mem(array $arr) : array

return $arr;

}

如果有io操作,比如資料庫操作,http呼叫操作(進一步可以使用try catch)

函式的返回值標識執行結果的成功與失敗,函式的返回引數通過傳進去乙個空的變數,通過這個變數拿出來

function test_io(array &$resp, array $params) : bool

return true;

}

php原生函式,ksort函式的引數就是引用傳值

<?php

function test_go(array $params) : array

$ret['data'] = $params;

$ret['msg'] = 'success';

$ret['status'] = true;

return $ret;

}$data = ;

$msg = '';

$status = false;

$params = [1,2,3,4,5,6];

[$data, $msg, $status] = test_go($params);

深入理解php7核心之zval

比如這種

struct _zval_struct  str;

hashtable *ht;

zend_object_value obj;

zend_ast *ast;

} value;

zend_uint refcount__gc;

zend_uchar type;

zend_uchar is_ref__gc;

};

簡單介紹下for each迴圈的使用

通常我們使用的迴圈語句有3,分別是 1.for int i 0 i 2.while 3.do while 常用一般是for迴圈。但是,在工作中,大量複寫for既繁瑣,又容易導致出錯 博主經常分不清哪個 是哪個的 所以經過老師的講解,學習了for each的使用。列子1 如果申明乙個陣列字串,如何使用...

涉及 C 的 foreach問題

當時是用foreach實現遍歷,但是函式傳入引數是object型別的,由於objectl型別沒有實現相關介面,所以foreach並不能執行。那麼下面我們來看看,想要使用foreach需要具備什麼條件。需要實現ienumerable介面或宣告getenumerator方法的型別。下面我們來看看fore...

foreach中引用 的問題

在工作中遇到 關於 php foreach 引用的乙個問題 簡單來說,如下 arr array a b c d foreach arr as k v foreach arr as k v print r arr 結果是 ab cc參考了 這篇文章,以及 這篇文章 在第一次foreach 遍歷結束後,...