常量,顧名思義是乙個常態的量值

2021-06-18 16:51:29 字數 3361 閱讀 2514

第二節 常量

常量,顧名思義是乙個常態的量值。它與值只繫結一次,它的作用在於有肋於增加程式的可讀性和可靠性。在php中,常量的名字是乙個簡單值的識別符號,在指令碼執行期間該值不能改變。和變數一樣,常量預設為大小寫敏感,但是按照我們的習慣常量識別符號總是大寫的。常量名和其它任何 php 標籤遵循同樣的命名規則。合法的常量名以字母或下劃線開始,後面跟著任何字母,數字或下劃線。在這一小節我們一起看下常量與我們常見的變數有啥區別,它在執行期間的不可改變的特性是如何實現的以及常量的定義過程。

首先看下常量與變數的區別,常量是在變數的zval結構的基礎上新增了一額外的元素。如下所示為php中常量的內部結構。

常量的內部結構

typedef struct _zend_constant zend_constant;

在zend/zend_constants.h檔案的33行可以看到如上所示的結構定義。在常量的結構中,除了與變數一樣的zval結構,它還包括屬於常量的標記,常量名以及常量所在的模組號。

在了解了常量的儲存結構後,我們來看php常量的定義過程。乙個例子。

define('tipi', 'thinking in php internal');

這是乙個很常規的常量定義過程,它使用了php的內建函式define。常量名為tipi,值為乙個字串,存放在zval結構中。從這個例子出發,我們看下define定義常量的過程實現。

define定義常量的過程

define是php的內建函式,在zend/zend_builtin_functions.c檔案中定義了此函式的實現。如下所示為部分原始碼:

/* ... // 類常量定義 此處不做介紹

... // 值型別判斷和處理

c.value = *val;

zval_copy_ctor(&c.value);

if (val_free)

c.flags = case_sensitive; /* non persistent */

c.name = zend_strndup(name, name_len);

c.name_len = name_len+1;

c.module_number = php_user_constant;

if (zend_register_constant(&c tsrmls_cc) == success) else

}/* }}} */

上面的**已經對物件和類常量做了簡化處理,其實現基本上是乙個將傳遞的引數傳遞給新建的zend_constant結構,並將這個結構體註冊到常量列表中的過程。關於大小寫敏感,函式的第三個引數表示是否大小不敏感,預設為false(大小寫敏感)。這個引數最後會賦值給zend_constant結構體的flags欄位。其在函式中實現**如下:

zend_bool non_cs = 0;   //  第三個引數的臨時儲存變數

int case_sensitive = const_cs;  //  是否大小寫敏感,預設為1

if(non_cs)

c.flags = case_sensitive; //     賦值給結構體欄位

從上面的define函式的實現來看,php對於常量的名稱在定義時其實是沒有所謂的限制。如下所示**:

define('^_^', 'smile');

if (defined('^_^')) else

通過defined函式測試表示,『^_^』這個常量已經定義好,只是這樣的常量無法呼叫。因為在作為語法解析時會顯示錯誤。在上面的**中有用到乙個判斷常量是否定義的函式,下面我們看看這個函式是如何實現的。

defined判斷常量是否設定

和define一樣, defined的實現也在zend/zend_builtin_functions.c檔案,其實現是乙個讀取引數變數,呼叫 zend_get_constant_ex函式獲取常量的值來判斷常量是否存在的過程。而zend_get_constant_ex函式不僅包括了常規的常規的常量獲取,還包括類常量的獲取,最後是通過zend_get_constant函式獲取常量的值。在zend_get_constant函式中,基本上是通過下面的**來獲取常量的值。

zend_hash_find(eg(zend_constants), name, name_len+1, (void **) &c)

除此之外,只是呼叫這個函式之前和之後對name有一些特殊的處理。

標準常量的初始化

以上通過define定義的常量的模組編號都是php_user_constant,這表示是使用者定義的常量。除此之外我們在平時使用較多的,如在顯示所有級別錯誤報告時使用的e_all常量就有點不同了。這裡我們以cgi模式為例說明標準常量的定義過程。整個呼叫順序如下所示:

[php_cgi_startup() -> php_module_startup() -> zend_startup() -> zend_register_standard_constants()]

void zend_register_standard_constants(tsrmls_d)

register_main_long_constant巨集展開是以zend_register_long_constant實現。 zend_register_long_constant函式將常量中值的型別,值,名稱及模組號賦值給新的zend_constant。並呼叫zend_register_constant新增到全域性的常量列表中。

[php_cgi_startup() -> php_module_startup() -> zend_startup() -> zend_register_standard_constants() -> zend_register_constant]

zend_api void zend_register_long_constant(const char *name, uint name_len,

long lval, int flags, int module_number tsrmls_dc)

zend_register_constant函式首先根據常量中的c->flags判斷是否區分大小寫,如果不區分,則名字統一為小寫,如果包含"\\",也統一成小寫。否則為定義的名字然後將呼叫下面的語句將當前常量新增到eg(zend_constants)。eg(zend_constants)是乙個hashtable(這在前面的章節中說明),下面的**是將常量新增到這個hashtable中。

zend_hash_add(eg(zend_constants), name, c->name_len, (void *) c,

sizeof(zend_constant), null)==failure)

在php_module_startup函式中,除了zend_startup函式中有註冊標準的常量,它本身體通過巨集register_main_long_constant等註冊了一些常量,如:php_version,php_os等。

關於介面和類中的常量我們將在後面的類所在章節中詳細說明。

乙個字面常量

c 有兩種常數 文字,符號。常量字面值常量文字數字插入 他們是常數,因為你不能改變他們的價值觀。1int x 5 5是乙個字面常量 常量可以字尾,確定其型別。整數常量可以有乙個u或u字尾,意味著他們是無符號的。整數常量也可以有乙個l或者l字尾,這意味著他們是長整數。然而,這些字尾通常是可選的,因為編...

前端如何定義乙個常量

為什麼會這樣?實際上,const定義的變數儲存的是指向實際資料的指標,對於基本資料型別string boolean number undefined null symbol而言,其值儲存在棧記憶體中的簡單資料段,按值訪問,就是等同於常量。但是相對於引用資料型別而言,const只能保證指向儲存在堆記憶...

海思的乙個 Makefile 解析

rm f mp4include makefile.param在makefile也和c語言一樣有include的用法,include後面跟乙個makefile檔名,其功能和c語言中的include類似,就是把被包含的makefile檔案的內容全部原本的複製到包含檔案中來。這個用法多用於在乙個專案中有多...