PHP扩展开发-数组的使用以及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 0) { /* Let's double the table size */
t = (Bucket**) perealloc_recoverable(ht->arBuckets,
(ht->nTableSize persistent);
if (t) {
HANDLE_BLOCK_INTERRUPTIONS();
ht->arBuckets = t;
ht->nTableSize = (ht->nTableSize
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++;
}
ht->nTableSize = 1
}
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
#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吗?

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









PHP 8.4 では、いくつかの新機能、セキュリティの改善、パフォーマンスの改善が行われ、かなりの量の機能の非推奨と削除が行われています。 このガイドでは、Ubuntu、Debian、またはその派生版に PHP 8.4 をインストールする方法、または PHP 8.4 にアップグレードする方法について説明します。

Visual Studio Code (VS Code とも呼ばれる) は、すべての主要なオペレーティング システムで利用できる無料のソース コード エディター (統合開発環境 (IDE)) です。 多くのプログラミング言語の拡張機能の大規模なコレクションを備えた VS Code は、

あなたが経験豊富な PHP 開発者であれば、すでにそこにいて、すでにそれを行っていると感じているかもしれません。あなたは、運用を達成するために、かなりの数のアプリケーションを開発し、数百万行のコードをデバッグし、大量のスクリプトを微調整してきました。

このチュートリアルでは、PHPを使用してXMLドキュメントを効率的に処理する方法を示しています。 XML(拡張可能なマークアップ言語)は、人間の読みやすさとマシン解析の両方に合わせて設計された多用途のテキストベースのマークアップ言語です。一般的にデータストレージに使用されます

JWTは、JSONに基づくオープン標準であり、主にアイデンティティ認証と情報交換のために、当事者間で情報を安全に送信するために使用されます。 1。JWTは、ヘッダー、ペイロード、署名の3つの部分で構成されています。 2。JWTの実用的な原則には、JWTの生成、JWTの検証、ペイロードの解析という3つのステップが含まれます。 3. PHPでの認証にJWTを使用する場合、JWTを生成および検証でき、ユーザーの役割と許可情報を高度な使用に含めることができます。 4.一般的なエラーには、署名検証障害、トークンの有効期限、およびペイロードが大きくなります。デバッグスキルには、デバッグツールの使用とロギングが含まれます。 5.パフォーマンスの最適化とベストプラクティスには、適切な署名アルゴリズムの使用、有効期間を合理的に設定することが含まれます。

文字列は、文字、数字、シンボルを含む一連の文字です。このチュートリアルでは、さまざまな方法を使用してPHPの特定の文字列内の母音の数を計算する方法を学びます。英語の母音は、a、e、i、o、u、そしてそれらは大文字または小文字である可能性があります。 母音とは何ですか? 母音は、特定の発音を表すアルファベットのある文字です。大文字と小文字など、英語には5つの母音があります。 a、e、i、o、u 例1 入力:string = "tutorialspoint" 出力:6 説明する 文字列「TutorialSpoint」の母音は、u、o、i、a、o、iです。合計で6元があります

静的結合(静的::) PHPで後期静的結合(LSB)を実装し、クラスを定義するのではなく、静的コンテキストで呼び出しクラスを参照できるようにします。 1)解析プロセスは実行時に実行されます。2)継承関係のコールクラスを検索します。3)パフォーマンスオーバーヘッドをもたらす可能性があります。

PHPの魔法の方法は何ですか? PHPの魔法の方法には次のものが含まれます。1。\ _ \ _コンストラクト、オブジェクトの初期化に使用されます。 2。\ _ \ _リソースのクリーンアップに使用される破壊。 3。\ _ \ _呼び出し、存在しないメソッド呼び出しを処理します。 4。\ _ \ _ get、dynamic属性アクセスを実装します。 5。\ _ \ _セット、動的属性設定を実装します。これらの方法は、特定の状況で自動的に呼び出され、コードの柔軟性と効率を向上させます。
