ホームページ >バックエンド開発 >PHPチュートリアル >PHP 拡張機能の開発 - 配列の使用と HashTable_PHP チュートリアルの概要

PHP 拡張機能の開発 - 配列の使用と HashTable_PHP チュートリアルの概要

WBOY
WBOYオリジナル
2016-07-14 10:08:34903ブラウズ

1 アレイ

このセクションでは、PHP 配列について説明します。PHP では、配列は HashTable を使用して実装されます。このセクションでは、まず HashTable について詳しく紹介し、その後、HashTable の使用方法について説明します
1.1 可変長構造
いわゆる可変長構造は、実際には C 言語構造の特別な使用法であり、それについては何も目新しいものではありません。まずは可変長構造体の一般的な定義方法を見てみましょう。
typedef 構造体バケット {
int;
文字キー[30];
文字値[1];
}バケツ;
バケット構造を定義しました。この構造を使用して生徒の個人プロファイルを保存したいと考えています。キーは学生の名前を保存するために使用され、値は学生のプロファイルを保存するために使用されます。興味があるかもしれませんが、この値は長さ 1 を宣言しています。1 文字にどれだけの情報を格納できるでしょうか?
実際、長い構造の場合、次のような変数を直接定義することはできません。 このように使用すると、Value には多くの情報が格納されなくなります。可変長構造体の場合、使用するときにまず可変長構造体へのポインタを宣言し、次に malloc 関数を通じて関数空間を割り当てる必要があります。使用する必要がある空間の長さに応じて malloc することができます。一般的な使用方法は次のとおりです。
バケット* pバケット;
pBucket = malloc(sizeof(Bucket) + n * sizeof(char));
ここで、n は使用する値の長さです。このように使用すると、value が指す文字列はすぐに長くなりますか?
1.2 ハッシュテーブルの概要
まずHashTableの定義を見てみましょう
struct _hashtable;
typedef 構造体バケット {
ulong h;//要素が数値インデックスの場合に使用されます
uint nKeyLength;//文字列インデックスを使用する場合、この変数はインデックスの長さを表し、インデックス (文字列) は最後の要素 aKey に格納されます
void *pData;//保存されたデータがポインタの場合、pDataPtr はこのデータを指し、pData は pDataPtr を指します。
void *pDataPtr;
structbucket *pListNext; //前の要素
;
struct Bucket *pListLast; //次の要素
structbucket *pNext; //次のバケットへのポインタ
structbucket *pLast //前のバケットへのポインタ
char arKey[1] //主に可変長構造を実現するために最後に配置する必要があります
}バケツ;
typedef struct _hashtable {
uint nTableSize //ハッシュテーブルのサイズ
uint nTableMask; //数値的には nTableSize- 1 に等しい
uint nNumOfElements; // 現在の HashTable に保存されているレコードの数を記録します
ulong nNextFreeElement //次の空きバケットを指します
Bucket *pInternalPointer; //この変数は配列の反転に使用されます
Bucket *pListHead //バケットの先頭を指します
Bucket *pListTail // Bucket の末尾を指します
バケット **arBuckets;
dtor_func_t pDestructor; // 配列の追加、削除、変更、チェック時に自動的に呼び出され、特定のクリーニング操作に使用される関数ポインター
zend_bool // 永続的ですか?
unsigned char nApplyCount;
zend_bool bApplyProtection; // nApplyCount と連携して、配列の走査中の無限再帰を防止します
#if ZEND_DEBUG
一貫性がありません;
#endif
}ハッシュテーブル;
上記の定義をよく見ていただければと思いますが、説明すると理解できないところがいくつかありますので、コードを見ていただいたほうがわかりやすいと思います。 PHP の配列は実際にはヘッド ノードを持つ二重リンク リストであり、HashTable がヘッドで、Bucket には特定のノード情報が格納されます。
1.3 HashTableの内部関数解析
1.3.1 マクロ HASH_PROTECT_RECURSION
#defineHASH_PROTECT_RECURSION(ht)
if((ht) - > bapplyprotection){
zend_error(E_ERROR, "ネストレベルが深すぎます - 再帰的な依存関係?");
}
このマクロは主に循環参照を防ぐために使用されます。
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)
{
バケツ **t;
IS_CONSISTENT(ht);
if ((ht->nTableSize << 1) > 0) { /* テーブルサイズを 2 倍にしましょう */
t = (バケット**) perrealloc_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();
成功を返す;
}
返品失敗;
}
成功を返します;
}
上記のコードから、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 永続的 ZEND_FILE_LINE_DC)
{
uint i = 3; //デフォルトでは HashTable のサイズは 2 の 3 乗ではありません
バケツ**tmp;
SET_INCONSISTENT(HT_OK);
if (nSize >= 0x80000000) {
ht->nTableSize = 0x80000000;
} 他 {
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 = 永続的;
ht->nApplyCount = 0;
ht->bApplyProtection = 1;
/* ecalloc() を使用して、Bucket* == NULL */
if (永続的) {
tmp = (バケット **) calloc(ht->nTableSize, sizeof(Bucket*));
if (!tmp) {
失敗を返す;
}
ht->arBuckets = tmp;
} 他 {
tmp = (バケット **) ecalloc_rel(ht->nTableSize, sizeof(Bucket*));
if (tmp) {
ht->arBuckets = tmp;
}
}
成功を返します;
}
HashTable のサイズが 2 の n 乗に初期化されていることがわかります。さらに、2 つのメモリ メソッドがあることがわかります。1 つは calloc で、もう 1 つは ecalloc_rel です。これら 2 つのメモリ割り当てメソッドについて詳しく説明しました。興味があれば、自分で調べてみてください。
1.3.4 Function_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)
{
うろんh;
uint nIndex;
バケツ *p;
IS_CONSISTENT(ht);
if (nKeyLength <= 0) {
#if ZEND_DEBUG
ZEND_PUTS("zend_hash_update: 空のキーンを置くことはできません");
#endif
返品失敗;
}
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 (フラグ & HASH_ADD) {
失敗を返す;
}
HANDLE_BLOCK_INTERRUPTIONS();
#if ZEND_DEBUG
if (p->pData == pData) {
ZEND_PUTS("zend_hash_update:p->pData == pDatan");
HANDLE_UNBLOCK_INTERRUPTIONS();
失敗を返す;
}
#endif
if (ht->pDestructor) {
ht->pDestructor(p->pData);
}
UPDATE_DATA(ht, p, pData, nDataSize);
if (pDest) {
*pDest = p->pData;
}
HANDLE_UNBLOCK_INTERRUPTIONS();
成功を返す;
}
}
p = p->pNext;
}
p = (バケット *) pemalloc(sizeof(バケット) - 1 + nKeyLength, ht->persistent);
if (!p) {
返品失敗;
}
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;
}
ハンドル_ブロック_中断();
CONNECT_TO_GLOBAL_DLLIST(p, ht);
ht->arBuckets[nIndex] = p;
HANDLE_UNBLOCK_INTERRUPTIONS();
ht->nNumOfElements++;
ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* ハッシュ テーブルがいっぱいの場合は、サイズを変更します */
成功を返します;
}
1.3.5 マクロ CONNECT_TO_BUCKET_DLLIST
#define CONNECT_TO_BUCKET_DLLIST(要素, list_head)
(要素)->pNext= (list_head);
(要素)->pLast= NULL;
if((要素)->pNext) {
(要素)->pNext->pLast =(要素);
}
このマクロはバケットをバケットリストに追加するものです
1.3.6 その他の関数またはマクロ定義
HashTable について簡単に紹介します。HashTable を詳しく理解したい場合は、HashTable のコードは Zend/zend_hash.h と Zend/zend_hash.c にあります。
zend_hash_add_empty_element は関数に空の要素を追加します
zend_hash_del_key_or_index はインデックスに基づいて要素を削除します
zend_hash_reverse_apply 逆走査ハッシュテーブル
zend_hash_copy コピー
_zend_hash_merge マージ
zend_hash_find 文字列インデックス検索
zend_hash_index_find 数値インデックス方式検索
zend_hash_quick_find 上記 2 つの関数のカプセル化
zend_hash_exists インデックスが存在するかどうか
zend_hash_index_exists インデックスが存在するかどうか
zend_hash_quick_exists は上記の 2 つのメソッドをカプセル化します
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”] = 'バズ';
add_assoc_string(arr, “mno”, “baz”, 1);
$arr[‘pqr’] = $myvar;
add_assoc_zval(arr, “pqr”, myvar);
1.5 タスクと実験
ここまで述べたので、実験してみましょう。
タスク: 配列を返します。配列内のデータは次のとおりです。
配列
(
[0] => テスト用
[42] => 123
[テスト用。テスト用。] => 1
[配列] => 配列
(
& [0] = & gt;
)
)
コードの実装:
PHP_FUNCTION(テスト)
{
zval* t;
array_init(return_value);
add_next_index_string(return_value, "テスト用", 1);
add_index_long(return_value, 42, 123);
add_assoc_double(return_value, "テスト用。テスト用。", 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 を覚えていますか?

www.bkjia.comtru​​ehttp://www.bkjia.com/PHPjc/477779.html技術記事 1 配列 このセクションでは、PHP 配列について説明します。PHP では、配列は HashTable を使用して実装されます。このセクションでは、まず HashTable について詳しく紹介し、次に HastTable 1.1 の使用方法について説明します...
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。