1 數組本節我們講一下php的數組,在php中,數組使用HashTable實現的。本節中我們先詳細的介紹一下HashTable,然後再講講如何使用HastTable1.1 變長結構體所謂的變長結構體,其實是我們C語言結構體的一種特殊用法,並沒有什麼新奇之處。我們先來看一下變長結構體的一種通用定義方法。typedef struct bucket { int n; char key[30]; char value[1];} Bucket;我們定義了一個結構體Bucket,我們希望用這個結構體存放學生的簡歷。其中key用來存在學生的姓名,value用來存放學生的簡介。大家可能很好奇,我們的value聲明了長度為1. 1個char能存多少資訊呀? 其實,對於變長結構體,我們在使用的使用不能直接定義變數,例如:Bucket bucket; 您要是這樣使用,value肯定儲存不了多少資訊。對於變長結構體,我們在使用的時候需要先聲明一個變長結構體的指標,然後通過malloc函數分配函數空間,我們需要用到的空間長度是多少,我們就可以malloc多少。通用的使用方法如下:Bucket* pBucket;pBucket = malloc(sizeof(Bucket) + n * sizeof(char));其中n就是你要使用value的長度。如果這樣使用的話,value指向的字串不久變長了嗎! 1.2 Hashtable簡介我們先看一下HashTable的定義struct _hashtable; typedef struct bucket { ulong h;//當元素是數字索引時使用 uint nKeyLength;//當使用字串索引時,這個變數表示索引的長度,索引(字串)儲存在最後一個元素aKey void *pData;//用來指向儲存的資料,如果儲存的資料是指標的話,pDataPtr就指向這個資料,pData指向pDataPtr void *pDataPtr; struct bucket *pListNext; //上一個元素 struct bucket *pListLast; //下一個元素 struct bucket *pNext; //指向下一個bucket的指標 struct bucket *pLast; //指向上一個bucket的指標 char arKey[1]; //必須放在最後,主要是為了實現變長結構體} Bucket; typedef struct _hashtable { uint nTableSize; //雜湊表的大小 uint nTableMask; //數值上等於nTableSize- 1 uint nNumOfElements; //記錄了當前HashTable中儲存的記錄數 ulong nNextFreeElement; //指向下一個閒置Bucket Bucket *pInternalPointer; //這個變數用於數組反轉 Bucket *pListHead; //指向Bucket的頭 Bucket *pListTail; //指向Bucket的尾 Bucket **arBuckets; dtor_func_t pDestructor; //函數指標,數組增刪改查時自動調用,用於某些清理操作 zend_bool persistent; //是否持久 unsigned char nApplyCount; zend_bool bApplyProtection; //和nApplyCount一起起作用,防止數組遍曆時無限遞迴#if ZEND_DEBUG int inconsistent;#endif} HashTable;希望大家能好好看看上面的定義,有些東西我將出來反而會說不明白,不如大家看看代碼淺顯明了。PHP的數組,其實是一個帶有頭結點的雙向鏈表,其中HashTable是頭,Bucket儲存具體的結點資訊。1.3 HashTable內建函式分析1.3.1 宏HASH_PROTECT_RECURSION#defineHASH_PROTECT_RECURSION(ht) \ if ((ht)->bApplyProtection) { \ if ((ht)->nApplyCount++ >= 3){ \ zend_error(E_ERROR, "Nestinglevel too deep - recursive dependency?"); \ } \ }這個宏主要用來防止循環參考。1.3.2 宏ZEND_HASH_IF_FULL_DO_RESIZE#defineZEND_HASH_IF_FULL_DO_RESIZE(ht) \ if ((ht)->nNumOfElements >(ht)->nTableSize) { \ zend_hash_do_resize(ht); \ } 這個宏的作用是檢查目前HashTable中的元素個數是否大於了總的HashTable的大小,如果個數大於了HashTable的大小,那麼我們就重新分配空間。我們看一下zend_hash_do_resizestatic int zend_hash_do_resize(HashTable *ht){ Bucket **t; IS_CONSISTENT(ht); if ((ht->nTableSize << 1) > 0) { /* Let's double the table size */ t = (Bucket**) perealloc_recoverable(ht->arBuckets,(ht->nTableSize << 1) * sizeof(Bucket *), ht->persistent); if (t) { HANDLE_BLOCK_INTERRUPTIONS(); ht->arBuckets = t; ht->nTableSize = (ht->nTableSize << 1); ht->nTableMask = ht->nTableSize - 1; zend_hash_rehash(ht); HANDLE_UNBLOCK_INTERRUPTIONS(); return SUCCESS; } return FAILURE; } return SUCCESS;} 從上面的代碼中我們可以看出,HashTable在分配空間的時候,新分配的空間等於原有空間的2倍。1.3.3 函數 _zend_hash_init這個函數是用來初始化HashTable的,我們先看一下代碼:ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC){ uint i = 3; //HashTable的大小預設無2的3次方 Bucket **tmp; SET_INCONSISTENT(HT_OK); if (nSize >= 0x80000000) { ht->nTableSize = 0x80000000; } else { while ((1U << i) < nSize) { i++; } ht->nTableSize = 1 << i; } ht->nTableMask = ht->nTableSize- 1; ht->pDestructor = pDestructor; ht->arBuckets = NULL; ht->pListHead = NULL; ht->pListTail = NULL; ht->nNumOfElements = 0; ht->nNextFreeElement = 0; ht->pInternalPointer = NULL; ht->persistent = persistent; ht->nApplyCount = 0; ht->bApplyProtection = 1; /* Uses ecalloc() so that Bucket* == NULL */ if (persistent) { tmp = (Bucket **) calloc(ht->nTableSize, sizeof(Bucket*)); if (!tmp) { return FAILURE; } ht->arBuckets = tmp; } else { tmp = (Bucket **) ecalloc_rel(ht->nTableSize, sizeof(Bucket*)); if (tmp) { ht->arBuckets = tmp; } } return SUCCESS;}可以看出,HashTable的大小被初始化為2的n次方,另外我們看到有兩種記憶體方式,一種是calloc,一種是ecalloc_rel,這兩中記憶體配置方式我們細講了,有興趣的話大家可以自己查一查。1.3.4 函數_zend_hash_add_or_update這個函數向HashTable中添加或者修改元素資訊ZEND_API int _zend_hash_add_or_update(HashTable *ht, const char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC){ ulong h; uint nIndex; Bucket *p; IS_CONSISTENT(ht); if (nKeyLength <= 0) {#if ZEND_DEBUG ZEND_PUTS("zend_hash_update: Can't put inempty key\n");#endif return FAILURE; } h = zend_inline_hash_func(arKey, nKeyLength); nIndex = h & ht->nTableMask; p = ht->arBuckets[nIndex]; while (p != NULL) { if ((p->h == h) && (p->nKeyLength == nKeyLength)) { if (!memcmp(p->arKey, arKey, nKeyLength)) { if (flag & HASH_ADD) { return FAILURE; } HANDLE_BLOCK_INTERRUPTIONS();#if ZEND_DEBUG if (p->pData == pData) { ZEND_PUTS("Fatal error in zend_hash_update:p->pData == pData\n"); HANDLE_UNBLOCK_INTERRUPTIONS(); return FAILURE; }#endif if (ht->pDestructor) { ht->pDestructor(p->pData); } UPDATE_DATA(ht, p, pData, nDataSize); if (pDest) { *pDest = p->pData; } HANDLE_UNBLOCK_INTERRUPTIONS(); return SUCCESS; } } p = p->pNext; } p = (Bucket *) pemalloc(sizeof(Bucket) - 1 + nKeyLength, ht->persistent); if (!p) { return FAILURE; } memcpy(p->arKey, arKey, nKeyLength); p->nKeyLength = nKeyLength; INIT_DATA(ht, p, pData, nDataSize); p->h = h; CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]); if (pDest) { *pDest = p->pData; } HANDLE_BLOCK_INTERRUPTIONS(); CONNECT_TO_GLOBAL_DLLIST(p, ht); ht->arBuckets[nIndex] = p; HANDLE_UNBLOCK_INTERRUPTIONS(); ht->nNumOfElements++; ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* If the Hash table is full, resize it */ return SUCCESS;}1.3.5 宏CONNECT_TO_BUCKET_DLLIST#define CONNECT_TO_BUCKET_DLLIST(element, list_head) \ (element)->pNext= (list_head); \ (element)->pLast= NULL; \ if((element)->pNext) { \ (element)->pNext->pLast =(element); \ }這個宏是將bucket加入到bucket鏈表中1.3.6 其他函數或者宏定義我們只是簡單的介紹一下HashTable,如果你想細緻的瞭解HashTable的話,建議您看看php的原始碼,關於HashTable的代碼在Zend/zend_hash.h 和Zend/zend_hash.c中。zend_hash_add_empty_element 給函數增加一個空元素zend_hash_del_key_or_index 根據索引刪除元素zend_hash_reverse_apply 反向遍曆HashTablezend_hash_copy 拷貝_zend_hash_merge 合并zend_hash_find 字串索引方式尋找zend_hash_index_find 數值索引方法尋找zend_hash_quick_find 上面兩個函數的封裝zend_hash_exists 是否存在索引zend_hash_index_exists 是否存在索引zend_hash_quick_exists 上面兩個方法的封裝1.4 C擴充常用HashTable函數雖然HashTable看起來有點複雜,但是使用卻是很方便的,我們可以用下面的函數對HashTable進行初始化和賦值。2005 年地方院校招生人數PHP文法C文法意義$arr = array()array_init(arr);初始化數組$arr[] = NULL;add_next_index_null(arr); $arr[] = 42;add_next_index_long(arr, 42); $arr[] = true;add_next_index_bool(arr, 1); $arr[] = 3.14;add_next_index_double(3.14); $arr[] = ‘foo’;add_next_index_string(arr, “foo”, 1);1的意思進行字串拷貝$arr[] = $myvar;add_next_index_zval(arr, myvar); $arr[0] = NULL;add_index_null(arr, 0); $arr[1] = 42;add_index_long(arr, 1, 42); $arr[2] = true;add_index_bool(arr, 2, 1); $arr[3] = 3.14;add_index_double(arr, 3, 3,14); $arr[4] = ‘foo’;add_index_string(arr, 4, “foo”, 1); $arr[5] = $myvar;add_index_zval(arr, 5, myvar); $arr[“abc”] = NULL;add_assoc_null(arr, “abc”); $arr[“def”] = 711;add_assoc_long(arr, “def”, 711); $arr[“ghi”] = true;add_assoc_bool(arr, ghi”, 1); $arr[“jkl”] = 1.44;add_assoc_double(arr, “jkl”, 1.44); $arr[“mno”] = ‘baz’;add_assoc_string(arr, “mno”, “baz”, 1); $arr[‘pqr’] = $myvar;add_assoc_zval(arr, “pqr”, myvar); 1.5 任務和實驗說了這麼多,我們實驗一下。任務:返回一個數組,數組中的資料如下:Array( [0] => for test [42] => 123 [for test. for test.] => 1 [array] => Array ( [0] => 3.34 ))代碼實現:PHP_FUNCTION(test){ zval* t; array_init(return_value); add_next_index_string(return_value, "for test", 1); add_index_long(return_value, 42, 123); add_assoc_double(return_value, "for test. for test.", 1.0); ALLOC_INIT_ZVAL(t); array_init(t); add_next_index_double(t, 3.34); add_assoc_zval(return_value, "array", t);}很簡單吧,還記得return_value嗎?
http://www.bkjia.com/PHPjc/477779.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/477779.htmlTechArticle1 數組 本節我們講一下php的數組,在php中,數組使用HashTable實現的。本節中我們先詳細的介紹一下HashTable,然後再講講如何使用HastTable 1.1...