通常靜態變數是靜態分配的,他們的生命週期和程式的生命週期一樣長,只有在程式退出後才結束生命週期,這和區域性變數相反,有的語言中全域性變數也是靜態分配的,例如php和js中的全域性變數。
靜態變數可以分為:
最常見的是靜態區域性變數和靜態成員變數。區域性變數只有在函式執行時才會存在。通常,當乙個函式執行完畢,它的區域性變數的值就已經不存在了,而且變數所佔據的記憶體也被釋放,當下一次執行該過程的時候,所有的區域性變數將重新被初始化,如果某個靜態變數定義為靜態的,那它的值就不會在函式呼叫結束後釋放,而是繼續保留變數的值。
本文將介紹靜態區域性變數
先看一下php中區域性變數的使用:
function t()
t();
t();
t();
上面的程式會輸出1 2 3。從這個例子可以看出$i變數的值在改變後函式繼續執行還能訪問到,$i變數就像是只有函式t()才能訪問到的乙個全域性變數。那php是怎麼實現的呢?
static是php的關鍵字,我們從詞法分析,語法分析,中間**生成到執行中間**這幾個部分**整個過程。
1、詞法分析
首先檢視zend/zend_language_scanner.l檔案,搜尋static關鍵字,我們可以找到如下**:
"static"
2、語法分析
在詞法分析找到token後,通過這個token,在zend/zend_language_parser.y檔案中查詢,找到**如下:
| t_static static_var_list ';'
static_var_list:
static_var_list ',' t_variable
| static_var_list ',' t_variable '=' static_scalar
| t_variable
| t_variable '=' static_scalar
;
語法分析的過程中如果匹配到相應的模式則會進行相應的處理動作,通常是進行opcode的編譯,在本例中的static關鍵字匹配中,是由函式zend_do_fetch_static_variable處理的。
3、生成opcode中間**
zend_do_fetch_static_variable函式的作用就是生成opcode,定義如下:
void zend_do_fetch_static_variable(znode *varname, const znode
*static_assignment, int fetch_type tsrmls_dc)
else
if (!cg(active_op_array)->static_variables)
// 8
ʦɖļļůė7
zend_hash_update(cg(active_op_array)->static_variables, varname-
>u.constant.value.str.val,
varname->u.constant.value.str.len+1, &tmp, sizeof(zval *), null);
...//˯ɐ
opline = get_next_op(cg(active_op_array) tsrmls_cc);
opline->opcode = (fetch_type == zend_fetch_lexical) ? zend_fetch_r :
zend_fetch_w; /* îwfetch_type=zend_fetch_staticjĩnǭzend_fetch_w*/
opline->result.op_type = is_var;
opline->result.u.ea.type = 0;
opline->result.u.var = get_temporary_variable(cg(active_op_array));
opline->op1 = *varname;
set_unused(opline->op2);
opline->op2.u.ea.type = zend_fetch_static; /* fkƣ%ĉánuus */
result = opline->result;
if (varname->op_type == is_const)
fetch_******_variable(&lval, varname, 0 tsrmls_cc); /* relies on the fact
that the default fetch is bp_var_w */
if (fetch_type == zend_fetch_lexical) else
cg(active_op_array)->opcodes[cg(active_op_array)->last-1].result.u.ea.type
|= ext_type_unused;
}
從上面的**我們可知,在解釋成中間**時,靜態變數是存放在cg(active_op_array)->static_variable中的。並且生成中間**為zend_fetch_w和zend_assign_ref。其中zend_fetch_w中間**是在zend_do_fetch_static_variable中直接賦值,而zend_assign_ref中間**是在zend_do_fetch_static_variable中呼叫zend_do_assign_ref生成的。
4、執行中間**
opcode的編譯階段完成後就開始opcode的執行了。在zend/zend_vm_opcodes.**件中包含所有opcode的巨集定義,這些巨集沒有特殊含義,只是作為opcode的唯一標示,包含本例中相關的如下兩個巨集的定義:
#define zend_fetch_w 83
#define zend_assign_ref 39
深入理解php核心(三)概覽-php指令碼的執行一文中介紹了根據opcode查詢到相應處理函式的方法,通過中間**呼叫對映方法計算得此時zend_fetch_w對應的操作為zend_fetch_w_spec_cv_handler。其**如下:
static int zend_fastcall
zend_fetch_w_spec_cv_handler(zend_opcode_handler_args)
static int zend_fastcall zend_fetch_var_address_helper_spec_cv(int type,
zend_opcode_handler_args)
else
break;
empty_switch_default_case()}}
switch (opline->op2.u.ea.type)
break;}}
...// 省略
}
在上面的**中有乙個關鍵的函式zend_get_target_symbol_table。它的作用是獲取當前正在執行的目標符號表,而在函式執行時當前的op_array則是函式體本身,先看看zend_op_array的結構。
struct _zend_op_array
由上邊可以看到zend_op_array中包含function_name欄位,也就是當前函式的名稱。再看看獲取當前符號表的函式:
static inline hashtable *zend_get_target_symbol_table(const zend_op *opline,
const temp_variable *ts, int type, const zval *variable tsrmls_dc)
return eg(active_op_array)->static_variables;
break;
empty_switch_default_case()
}return null;
}
在當前的zend_do_fetch_static_variable執行時,op2.u.ea.type的值為zend_fetch_static,從而這zend_get_target_symbol_table函式我們返回的是eg(active_op_array)->static_variable。也就是當前函式的靜態變數雜湊表。每次執行時都會從該符號表中查詢相應的值,由於op_array在程式執行時始終存在。所有對靜態符號表中數值的修改會繼續保留,下次函式執行時繼續從符號表獲取資訊。也就是說zend為每個函式(準確的說是zend_op_array)分配了乙個私有的符號表來儲存該函式的靜態變數。 深入理解php核心
第二章 使用者 的執行 第三節 zend引擎與指令碼執行 第四節 小結 第三章 變數及資料型別 第二節 常量 第三節 預定義變數 第四節 靜態變數 第五節 型別提示的實現 第六節 變數的生命週期 第七節 資料型別轉換 第八節 小結 第四章 函式的實現 第二節 函式的定義,引數及返回值 第三節 函式的...
深入理解php核心
第二章 使用者 的執行 第三節 zend引擎與指令碼執行 第四節 小結 第三章 變數及資料型別 第二節 常量 第三節 預定義變數 第四節 靜態變數 第五節 型別提示的實現 第六節 變數的生命週期 第七節 資料型別轉換 第八節 小結 第四章 函式的實現 第二節 函式的定義,引數及返回值 第三節 函式的...
深入理解PHP核心 一
最近,和乙個交流的時候,給我提了乙個非常奇怪的問題。那就是,在乙個運算中,加了乙個引用之後,發現效能慢了一萬倍。在我的腦海裡面,引用是乙個非常容易出錯的問題,特別是php裡面的引用,有非常多的陷阱。因為,以前專門研究過這一塊php的源 所以,我可以比較清晰的解析引用到底是怎麼一回事,希望,讀了我這篇...