PHP擴充套件開發-08-陣列的使用以及HashTable簡介
1 陣列
本節我們講一下php的陣列,在php中,陣列使用HashTable實現的。本節中我們先詳細的介紹一下HashTable,然後再講講如何使用HastTable
1.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_resize
static 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,constchar *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反向遍歷HashTable
zend_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嗎?
呵呵,有問題聯絡我吧。不多說了 iamnew<[email protected]>或者QQ947847775相關推薦
PHP擴充套件開發-08-陣列的使用以及HashTable簡介
1 陣列 本節我們講一下php的陣列,在php中,陣列使用HashTable實現的。本節中我們先詳細的介紹一下HashTable,然後再講講如何使用HastTable 1.1 變長結構體 所謂的變長結構體,其實是我們C語言結構體的一種特殊用法,並沒
php擴充套件開發-陣列操作
//add_assoc_*系列函式,生成字元索引: add_assoc_null(zval *aval, char *key); add_assoc_bool(zval *aval, char *key, zend_bool bval); add_assoc_long(zval *ava
php擴充套件開發-變數設定和銷燬
//定義變數 zval *op; //分配記憶體 MAKE_STD_ZVAL(op); //賦值 ZVAL_LOGN(op, 0); //獲取 op 的值 if (Z_LVAL_P(op)) { php_printf("%s", "true"); } else { p
php擴充套件開發-錯誤處理
//輸出php錯誤 //NULL TSRMLS_CC 預設寫法 //E_NOTICE 錯誤級別 //錯誤內容 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "test"); //Notice: confirm_xing2233_compiled()
php 擴充套件開發講解網址 菜鳥學php擴充套件 之 hello world(一) 菜鳥學php擴充套件 之 自動生成的擴充套件框架詳解(二) 菜鳥學php擴充套件 之 詳解擴充套件函式的傳參(如何獲取引數)(三) 菜鳥學php擴充套件 之 詳解php擴充套件的變數(四) 菜鳥學php擴充套件 之
菜鳥學php擴充套件 之 hello world(一) https://blog.csdn.net/u011957758/article/details/72234075 菜鳥學php擴充套件 之 自動生成的擴充套件框架詳解(二) https://blog.csdn.net/u
php擴充套件開發--快速入手
我們首先找到快速上手文章裡面關於函式定義的程式碼,以此說明然後開發PHP的函式 //php_myext.h PHP_FUNCTION(myext_hello);//函式申明,所有在myext.c檔案定義的函式,都會在這個檔案裡有一個申明 //myext.c static
php擴充套件開發筆記(9)sizeof 和 strlen 遇到空字元 '\0' 的問題
看看下面程式碼的不同表現 #include <stdio.h> #include <stdlib.h> #include <string.h> int m
Windows下php擴充套件開發c++動態庫
PHP擴充套件開發,從零瞭解到初步完成一個小專案,經過三天的仔細研究,現整理如下 一、需求介紹 PHP擴充套件開發,呼叫自己之前的c++動態庫,完成功能 二、專案之前 系統:windows xp 開發工具:vs 2008 web環境:apache2.4 PHP5.3.2
PHP 擴充套件開發之Zephir
最近對程式碼進行效能分析後,發現兩個耗時的地方:自動載入檔案數太多;引數驗證函式呼叫超過1000次。這也是許多php語言框架面臨的問題,所以發展出來諸如Yaf,Swoole,Phalcon這些C語言擴充套件框架,或者類似workerman,reactphp,phpdaemon這些一次載入的框架。總之減少載入檔
php擴充套件開發(一 基本介紹和擴充套件開發的基本步驟)
需要開發擴充套件的場景: 1)應用非常注重效率,如複雜的影象演算法,需要編寫成擴充套件模組 2)有些系統呼叫不能用PHP直接訪問,比如Linux下的fork函式建立一個程序,需要編寫擴充套件 3)需要商業化一個應用,又不想暴露原始碼 一,搭建PHP框架 1
PHP擴充套件開發之動態載入so模組與靜態重編譯PHP(上)
動態載入so模組:利用ext_skel工具編譯生成so模組,修改php.ini檔案,動態載入即可 靜態編譯:將編寫的模組靜態編譯到PHP,需要重新編譯PHP 假設要編寫一個my_dynamic擴充套件,呼叫my_dynamic函式後輸出字串“The Best
《PHP擴充套件開發及核心應用》學習筆記(二)
二、PHP變數在核心中的實現 在強型別的程式語言中,我們必須在使用變數前先宣告(定義)變數的型別和名稱。 而PHP屬於弱型別的程式語言,PHP 會根據變數的值,自動把變數轉換為正確的資料型別。 1. 變數的型別 PHP中,一共有8種資料型別: 包括4中標
一次失敗的PHP擴充套件開發之旅
2. 進入php原始碼包的ext目錄,藉助ext_skel工具生成外掛架子程式碼 cd ext ./ext_skel --extname=demo 3. 編輯config.m4,開啟PHP_ARG_WITH或者PHP_ARG_ENABLE選項(說實話區別仍沒搞清楚,求達人指點),新增C++支援、依賴路徑
PHP 擴充套件開發初探
什麼是 PHP 擴充套件 通俗說,PHP 擴充套件是增強 PHP 語言功能的外掛。PHP 提供了程式語言的語法,比如分支、迴圈、函式、類等,這些是 PHP 本身所提供的。在某些情況下需要在 PHP 語言的基礎上進行擴充套件,那麼就需要通過 PHP 底層提供的資料結構和介面來開發 PHP 擴充套件,從而來補充或
開發PHP擴充套件步驟
ubuntu14.04系統 用eclipse 安裝c/c++ php develop tools 語言開發外掛 安裝三個開發工具 apt-get install gcc make autoconfig 下載原始碼 匯入到eclpise 執行/h
php物件(object) 與 陣列(array) 的區別以及轉換
php中物件和陣列的區別 物件不僅有屬性,還有方法;,物件可以封裝對這些資料的操作,而陣列是辦不到的。 因此:如果是單純存資料,就用陣列,但如果你要定義對這些資料的操作,更建議使用物件! 舉例: 如果你想在操作的資料里加個通用的方法,比如返回值是加密的,或者其它等等功能,這時
PHP-CPP開發擴充套件(六)
PHP-CPP是一個用於開發PHP擴充套件的C++庫。本節講解在C++中PHP異常、變數、常量的實現相關知識。 異常 PHP和C++都支援異常,而PHP-CPP庫這兩種語言之間的異常處理是完全透明的。你在C++中丟擲的異常會自動傳遞給PHP指令碼,並且你的C++程式碼可以捕獲PHP指令碼丟擲的異常,就像它是普
php獲取兩個陣列相同的元素(交集)以及比較兩個陣列中不同的元素(差集)
(一)php獲取兩個陣列相同元素 array array_intersect(array $array1, array $array2, [, array $...]) array array_int
Tokyo Cabinet與Tokyo Tyrant的安裝以及PHP擴充套件
wget http://fallabs.com/tokyocabinet/tokyocabinet-1.4.47.tar.gz tar zxvf tokyocabinet-1.4.47.tar.gz cd tokyocabinet-1.4.47 ./configure --prefix=/usr/loc
原創 快速開發一個PHP擴充套件
快速開發一個PHP擴充套件本文通過非常快速的方式講解了如何製作一個PHP 5.2 環境的擴充套件(PHP Extension),希望能夠在圖文的方式下讓想快速學習的朋友瞭解一下製作過程。需求:比如開發一個叫做 heiyeluren 的擴充套件,擴充套件裡就一個函式 heiye