PHP 의 HashTable 구현
20988 단어 Hashtable
데이터 구조:
HashTable 데이터 구조 에 대한 설명 은 Zend / zendhash. h 파일 중.우선, HashTable 의 모든 요 소 는 아래 의 struct 에 저 장 됩 니 다.
typedef struct bucket {
ulong h; /* hash , ,h */
uint nKeyLength; /* key , nKeyLength 0 */
void *pData; /* */
void *pDataPtr; /* */
struct bucket *pListNext; /* */
struct bucket *pListLast; /* */
struct bucket *pNext; /* Hash */
struct bucket *pLast; /* Hash */
const char *arKey; /* key */
} Bucket; PHP 는 양 방향 링크 와 Hash 표를 결합 시 키 는 방식 으로 Hash Table 을 실현 합 니 다. 이 로 인해 Hash Table 은 O (1) 의 시간 복잡 도 에서 임의의 key 조 회 를 실현 할 수 있 고 Hash Table 의 스 트 리밍 도 지원 할 수 있 습 니 다.
다음은 Hash Table 의 정 의 를 살 펴 보고 대체적으로 주석 을 달 았 습 니 다. 새 형의 주석 과 같 습 니 다.
typedef struct _hashtable {
uint nTableSize; /* hash */
uint nTableMask; /* , hash , nTableSize-1 */
uint nNumOfElements; /* hash */
ulong nNextFreeElement; /* */
Bucket *pInternalPointer; /* , HashTable */
Bucket *pListHead; /* */
Bucket *pListTail; /* */
Bucket **arBuckets; /* bucket */
dtor_func_t pDestructor; /* */
zend_bool persistent;
unsigned char nApplyCount; /* */
zend_bool bApplyProtection;
#if ZEND_DEBUG
int inconsistent;
#endif
} HashTable; 위의 데이터 구 조 를 잘 이해 하기 위해 저 는 새 형의 손 그림 을 참조 하여 약간 아름 다운 관점 의 그림 을 그 려 서 위의 지침 의 역할 을 설명 합 니 다.
위의 그림 은 6 개의 요소 가 있 는 HashTable 이 며, 현재 두 번 째 요소 에 접근 하고 있 습 니 다. 왼쪽 arBuckets 포인터 목록 의 숫자 는 오른쪽 bucket 의 숫자 와 대응 하지 않 습 니 다.
PHP 코드 에서 모든 변 수 는 zval 의 구조 로 저장 되 고 zval 은 zvalue 를 사용 합 니 다.value 포인터 가 진정한 데 이 터 를 가리 키 는 것 입 니 다.zvalue_value 는 Union 구조 입 니 다. 이 구 조 는 HashTable 지침 을 포함 하기 때문에 PHP 의 변 수 는 HashTable 일 수 있 습 니 다. 그리고 실제로 우리 가 PHP 에서 사용 하 는 HashTable 은 HashTable 에 zval 구 조 를 포함 합 니 다.
struct _zval_struct {
zvalue_value value; /* */
zend_uint refcount__gc;
zend_uchar type; /* */
zend_uchar is_ref__gc;
};
typedef struct _zval_struct zval;
typedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len;
} str;
HashTable *ht; /* hash table value */
zend_object_value obj;
} zvalue_value; 해시 테이블 생 성:
HashTable 을 만 드 는 데 는 여러 가지 경로 가 있 지만 기본 적 인 절 차 는 통 합 됩 니 다. HashTable 형식의 zval 을 만 드 는 것 을 예 로 들 어 설명 하 겠 습 니 다.
우선 표준 zval 을 만들어 야 합 니 다. 즉, 이 zval 에 메모리 공간 을 신청 하 는 것 입 니 다.
zval *array;
MAKE_STD_ZVAL(array); /* zval , zval array */ MAKE_STD_ZVAL 은 표준 zval 을 만 드 는 매크로 입 니 다. 위의 코드 는 다음 과 같 습 니 다.
array = (zval *) emalloc(sizeof(zval));
array->refcount__gc = 1;
array->is_ref__gc = 0; 그리고 array 를 호출 할 수 있 습 니 다.init 에서 이 해시 테이블 을 초기 화 합 니 다:
array_init(array); 이상 코드 는:
(*array).value.ht = (HashTable *) emalloc_rel(sizeof(HashTable));
_zend_hash_init((*array).value.ht, 0, NULL, ZVAL_PTR_DTOR, 0 ZEND_FILE_LINE_RELAY_CC);
(*array).type = IS_ARRAY; 초기 화 과정 은 다음 과 같이 요약 할 수 있 습 니 다.
그 중 포 인 트 는zend_hash_init 함수 입 니 다.HashTable 형식의 zval 을 만 들 필요 가 없다 면 HashTable 을 만 들 뿐 입 니 다. zend 를 호출 해 야 합 니 다.hash_init 이 매크로 를 초기 화 합 니 다:
HashTable *ht;
ALLOC_HASHTABLE(ht);
zend_hash_init(ht, 0, NULL, NULL, 0); 여기 제 가 zend 를 간소화 해 봤 어 요.hash_init 호출 인자, ALLOCHASHTABLE 은 이 HashTable 에 메모리 공간 을 신청 하고 zendhash_init 는 매크로 입 니 다. 실제로 위의 을 호출 합 니 다.zend_hash_init 함수, 그럼 이 함수 가 무엇 을 했 는 지 살 펴 보 겠 습 니 다.
ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_D
{
uint i = 3; /* HashTable , 2 , 2 3 */
SET_INCONSISTENT(HT_OK); /* , HashTable */
if (nSize >= 0x80000000) {
/* HashTable , */
ht->nTableSize = 0x80000000;
} else {
while ((1U << i) < nSize) { /* size 2 HashTable */
i++;
}
ht->nTableSize = 1 << i;
}
ht->nTableMask = 0; /* 0 ht->arBuckets */
ht->pDestructor = pDestructor; /* */
ht->arBuckets = (Bucket**)&uninitialized_bucket;
ht->pListHead = NULL;
ht->pListTail = NULL;
ht->nNumOfElements = 0;
ht->nNextFreeElement = 0;
ht->pInternalPointer = NULL;
ht->persistent = persistent;
ht->nApplyCount = 0;
ht->bApplyProtection = 1;
return SUCCESS;
} 코드 에서 pHash Function 이라는 매개 변 수 는 이미 사용 하지 않 습 니 다. 단지 아래 호 환 을 유지 하기 위해 서 입 니 다.pDestructor 는 HashTable 요소 의 분석 지침 으로 HashTable 의 요 소 를 업데이트 / 소각 할 때 사용 합 니 다.
zend_hash_init 는 buckets 를 저장 하기 위해 메모리 공간 을 신청 하지 않 았 습 니 다. 초기 화 된 size 만 설정 하고 nTableMask 를 0 으로 설정 하여 ht - > arBuckets 가 초기 화 되 지 않 았 음 을 표시 합 니 다.전체 HashTable 의 size 는 2 의 정수 차 멱 에 따라 신청 한 것 입 니 다. 최소 2 의 3 차 멱 입 니 다. 공간 이 부족 하면 2 의 4 차 멱, 2 의 5 차 멱 을 시도 합 니 다. 들 어 오 는 size 보다 클 때 까지.
그럼 buckets 공간 은 언제 신청 하 셨 나 요?정 답 은 HashTable 을 조작 할 때 HashTable 을 만 드 는 범주 에 속 하지 않 는 다 는 것 이다. 다음은 HashTable 을 어떻게 조작 하 는 지 살 펴 보 자.
해시 테이블 동작:
요소 추가 / 업데이트:
HashTable 초기 화 후 zend 사용 가능hash_add 는 HashTable 에 원 소 를 추가 합 니 다. zendhash_add 는 매크로 입 니 다:
#define zend_hash_add(ht, arKey, nKeyLength, pData, nDataSize, pDest)
_zend_hash_add_or_update(ht, arKey, nKeyLength, pData, nDataSize, pDest, HASH_ADD ZEND_FILE_LINE_CC) 그럼 실제로 호출zend_hash_add_or_update 이 함수:
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;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
IS_CONSISTENT(ht);
if (nKeyLength <= 0) {
#if ZEND_DEBUG
ZEND_PUTS("zend_hash_update: Can't put in empty keyn");
#endif
return FAILURE;
}
CHECK_INIT(ht); /* buckets , buckets */
h = zend_inline_hash_func(arKey, nKeyLength); /* key hash */
nIndex = h & ht->nTableMask; /* key */
p = ht->arBuckets[nIndex]; /* bucket */
while (p != NULL) { /* , bucket */
if (p->arKey == arKey ||
((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
/* bucket key key , */
if (flag & HASH_ADD) { /* add , */
return FAILURE;
}
HANDLE_BLOCK_INTERRUPTIONS();
#if ZEND_DEBUG
if (p->pData == pData) {
ZEND_PUTS("Fatal error in zend_hash_update: p->pData == pDatan");
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; /* key key , Hash bucket
}
/* , key key , sizeof(bucket)+nKeyLength key */
if (IS_INTERNED(arKey)) {
p = (Bucket *) pemalloc(sizeof(Bucket), ht->persistent);
if (!p) {
return FAILURE;
}
p->arKey = arKey;
} else {
p = (Bucket *) pemalloc(sizeof(Bucket) + nKeyLength, ht->persistent);
if (!p) {
return FAILURE;
}
p->arKey = (const char*)(p + 1);
memcpy((char*)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;
} 제 가 주석 을 달 았 으 니 알 수 있 을 것 같 습 니 다. 그 중에서 중요 한 것 은 할당 을 수행 하 는 곳 입 니 다. INITDATA 라 는 매크로 는 이렇게 정의 합 니 다.
#define INIT_DATA(ht, p, pData, nDataSize);
if (nDataSize == sizeof(void*)) {
memcpy(&(p)->pDataPtr, pData, sizeof(void *));
(p)->pData = &(p)->pDataPtr;
} else {
(p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent);
if (!(p)->pData) {
pefree_rel(p, (ht)->persistent);
return FAILURE;
}
memcpy((p)->pData, pData, nDataSize);
(p)->pDataPtr=NULL;
} 여기에 tricks 가 있 습 니 다. PHP 는 데이터 의 크기 와 void 지침 을 판단 하 는 동시에 추가 공간 을 신청 하지 않 고 데 이 터 를 pDataPtr 필드 에 복사 합 니 다. 즉, HashTable 에 추 가 된 것 이 지침 이 라면 그 는 pDataPtr 세그먼트 에 직접 저장 되 고 pData 필드 도 저장 합 니 다.HashTable 에 더 큰 구 조 를 추가 하면 PHP 는 이 구 조 를 위해 메모리 공간 을 따로 신청 하고 데 이 터 를 새로 신청 한 메모리 공간 에 복사 한 다음 pDataPtr 를 NULL 로 설정 합 니 다.이것 은 새 형의 그 블 로그 에서 언급 되 었 다.
Bucket 에서 실제 데 이 터 는 pData 포인터 가 가리 키 는 메모리 블록 에 저 장 됩 니 다. 보통 이 메모리 블록 은 시스템 에서 따로 분 배 됩 니 다.그러나 한 가지 예외 가 있 습 니 다. Bucket 에 저 장 된 데이터 가 지침 일 때 HashTable 은 시스템 에 이 지침 을 저장 할 공간 을 따로 요청 하지 않 고 이 지침 을 pDataPtr 에 직접 저장 한 다음 pData 를 이 구조 구성원 의 주 소 를 가리 키 는 것 입 니 다.이렇게 하면 효율 을 높이 고 메모리 조각 을 줄 일 수 있다.이로써 우 리 는 PHP Hash Table 디자인 의 정교 한 점 을 볼 수 있다.Bucket 의 데이터 가 포인터 가 아니라면 pDataPtr 는 NULL 입 니 다 (이 단락 은 Altair 에서
우리 가 조작 하고 자 하 는 것 이 HashTable 형식의 zval 일 때 add 를 사용 할 수 있 습 니 다.assoc_*계열 함수:
add_assoc_null(zval *aval, char *key);
add_assoc_bool(zval *aval, char *key, zend_bool bval);
add_assoc_long(zval *aval, char *key, long lval);
add_assoc_double(zval *aval, char *key, double dval);
add_assoc_string(zval *aval, char *key, char *strval, int dup);
add_assoc_stringl(zval *aval, char *key,
char *strval, uint strlen, int dup);
add_assoc_zval(zval *aval, char *key, zval *value); 우 리 는 그 중의 한 함수 의 실현 을 보 았 다.
#define add_assoc_string(__arg, __key, __str, __duplicate) add_assoc_string_ex(__arg, __key, strlen(__key)+1, __str, __duplicate)
ZEND_API int add_assoc_string_ex(zval *arg, const char *key, uint key_len, char *str, int duplicate) /* {{{ */
{
zval *tmp;
MAKE_STD_ZVAL(tmp);
ZVAL_STRING(tmp, str, duplicate);
return zend_symtable_update(Z_ARRVAL_P(arg), key, key_len, (void *) &tmp, sizeof(zval *), NULL);
} 프로 세 스 는 들 어 오 는 char * 로 문자열 형식의 zval 을 만 든 다음 arg 의 아래 표 시 된 key 위 치 를 삽입 하 는 것 입 니 다. 중요 한 것 은 zend 입 니 다.symtable_update, 이것 은 방금 사용 한 것 과 그다지 다 릅 니 다. 그러면 이 함 수 는 무엇 을 했 습 니까?그것 도 실제로 zend 를 호출 했다.hash_update, 그 전에 ZEND 를 호출 했 을 뿐 입 니 다.HANDLE_NUMERIC 이 매크로:
static inline int zend_symtable_update(HashTable *ht, const char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest)
{
ZEND_HANDLE_NUMERIC(arKey, nKeyLength, zend_hash_index_update(ht, idx, pData, nDataSize, pDest));
return zend_hash_update(ht, arKey, nKeyLength, pData, nDataSize, pDest);
} ZEND_HANDLE_NUMERIC 의 역할 은 들 어 오 는 key 가 문자열 형식의 숫자 일 때 ulong 형식 으로 바 꾸 는 것 입 니 다.
마지막 으로 업데이트 와 관련 된 함수 나 매크로 를 시스템 적 으로 살 펴 보 겠 습 니 다.
/* additions/updates/changes */
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);
#define zend_hash_update(ht, arKey, nKeyLength, pData, nDataSize, pDest)
_zend_hash_add_or_update(ht, arKey, nKeyLength, pData, nDataSize, pDest, HASH_UPDATE ZEND_FILE_LINE_CC)
#define zend_hash_add(ht, arKey, nKeyLength, pData, nDataSize, pDest)
_zend_hash_add_or_update(ht, arKey, nKeyLength, pData, nDataSize, pDest, HASH_ADD ZEND_FILE_LINE_CC)
ZEND_API int _zend_hash_quick_add_or_update(HashTable *ht, const char *arKey, uint nKeyLength, ulong h, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC);
#define zend_hash_quick_update(ht, arKey, nKeyLength, h, pData, nDataSize, pDest)
_zend_hash_quick_add_or_update(ht, arKey, nKeyLength, h, pData, nDataSize, pDest, HASH_UPDATE ZEND_FILE_LINE_CC)
#define zend_hash_quick_add(ht, arKey, nKeyLength, h, pData, nDataSize, pDest)
_zend_hash_quick_add_or_update(ht, arKey, nKeyLength, h, pData, nDataSize, pDest, HASH_ADD ZEND_FILE_LINE_CC)
ZEND_API int _zend_hash_index_update_or_next_insert(HashTable *ht, ulong h, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC);
#define zend_hash_index_update(ht, h, pData, nDataSize, pDest)
_zend_hash_index_update_or_next_insert(ht, h, pData, nDataSize, pDest, HASH_UPDATE ZEND_FILE_LINE_CC)
#define zend_hash_next_index_insert(ht, pData, nDataSize, pDest)
_zend_hash_index_update_or_next_insert(ht, 0, pData, nDataSize, pDest, HASH_NEXT_INSERT ZEND_FILE_LINE_CC)
ZEND_API int zend_hash_add_empty_element(HashTable *ht, const char *arKey, uint nKeyLength); _zend_hash_add_or_update 아까 소 개 했 는데,zend_hash_index_update_or_next_insert 는 숫자 아래 에 표 시 된 요 소 를 추가 하거나 업데이트 하거나 HashTable 말미 에 요 소 를 추가 합 니 다. _zend_hash_quick_add_or_update 는 성능 을 고려 하여 같은 key 를 여러 번 조작 해 야 할 때 zend 를 먼저 이용 할 수 있 습 니 다.get_hash_value () 는 hash 값 을 계산 한 다음 매개 변수 에 들 어 옵 니 다. 그러면 매번 key 의 hash 값 을 계산 할 필요 가 없습니다.
요소 삭제:
원소 삭제 사용 zendhash_del 시리즈 의 매크로 와 함수, 그들의 정 의 는 다음 과 같 습 니 다.
ZEND_API int zend_hash_del_key_or_index(HashTable *ht, const char *arKey, uint nKeyLength, ulong h, int flag);
#define zend_hash_del(ht, arKey, nKeyLength)
zend_hash_del_key_or_index(ht, arKey, nKeyLength, 0, HASH_DEL_KEY)
#define zend_hash_quick_del(ht, arKey, nKeyLength, h)
zend_hash_del_key_or_index(ht, arKey, nKeyLength, h, HASH_DEL_KEY_QUICK)
#define zend_hash_index_del(ht, h)
zend_hash_del_key_or_index(ht, NULL, 0, h, HASH_DEL_INDEX) 다음 zendhash_del_key_or_index 함수 의 코드 는 다음 과 같 습 니 다.
ZEND_API int zend_hash_del_key_or_index(HashTable *ht, const char *arKey, uint nKeyLength, ulong h, int flag)
{
uint nIndex;
Bucket *p;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
IS_CONSISTENT(ht);
if (flag == HASH_DEL_KEY) { /* key , hash */
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)
&& ((p->nKeyLength == 0) /* , , key */
|| !memcmp(p->arKey, arKey, nKeyLength))) { /* key */
HANDLE_BLOCK_INTERRUPTIONS();
if (p == ht->arBuckets[nIndex]) { /* hash , hash */
ht->arBuckets[nIndex] = p->pNext;
} else {
p->pLast->pNext = p->pNext; /* pNext ( Hash ) */
}
if (p->pNext) {
p->pNext->pLast = p->pLast; /* */
}
if (p->pListLast != NULL) { /* , Hash */
p->pListLast->pListNext = p->pListNext;
} else {
/* Deleting the head of the list */
ht->pListHead = p->pListNext;
}
if (p->pListNext != NULL) {
p->pListNext->pListLast = p->pListLast;
} else {
ht->pListTail = p->pListLast;
}
if (ht->pInternalPointer == p) { /* , */
ht->pInternalPointer = p->pListNext;
}
if (ht->pDestructor) { /* */
ht->pDestructor(p->pData);
}
if (p->pData != &p->pDataPtr) { /* HashTable , */
pefree(p->pData, ht->persistent);
}
pefree(p, ht->persistent); /* bucket */
HANDLE_UNBLOCK_INTERRUPTIONS();
ht->nNumOfElements--;
return SUCCESS;
}
p = p->pNext;
}
return FAILURE;
} 우리 가 데 이 터 를 HashTable 에 추가 할 때, PHP 가 어떻게 메모 리 를 방출 하 는 지 주의해 야 합 니 다. 제 가 이전에 겪 었 던 문 제 는 주로 여기에 집중 되 어 있 습 니 다. 이것 은 몇 가지 상황 으로 나 누 어 토론 해 야 합 니 다.
상황 1: 제 가 메모 리 를 신 청 했 습 니 다. 포인터 p 로 이 메모 리 를 가리 키 고 zend 를 호출 합 니 다.hash_add 를 할 때 다음 코드 와 유사 하 게 p 를 HashTable 에 추가 합 니 다.
my_type *p;
p = malloc(sizeof(my_type));
zend_hash_add(ht, "key", sizeof("key"), (void **)&p, sizeof(p), NULL); PHP 는 먼저 sizeof (Bucket) + 4 의 공간 을 신청 하여 Bucket 을 저장 합 니 다. + 4 의 공간 은 Bucket 이 key 를 저장 하 는 데 사 용 됩 니 다. 그리고 PHP 는 그 시기 에 pData 에 들 어 오 는 것 이 지침 이기 때문에 PHP 는 pData 에 추가 공간 을 신청 하지 않 고 pDataPtr 와 pData 필드 에 직접 넣 을 것 이 라 고 판단 합 니 다.
요 소 를 삭제 할 때 PHP 는 Bucket 이 신청 한 메모 리 를 방출 합 니 다. 그러면 그 전에 석조 함 수 를 호출 하 는 절 차 는 우리 가 신청 한 메모 리 를 청소 할 수 있 는 유일한 기회 입 니 다.우 리 는 함 수 를 정의 하고 zend 에서hash_init 때 들 어 오기 때문에 코드 는 이렇게 보 여야 합 니 다.
zend_hash_init(ht, 0, NULL, my_free, 0);
my_type *p;
p = malloc(sizeof(my_type));
zend_hash_add(ht, "key", sizeof("key"), (void **)&p, sizeof(p), my_free);
/* definition */
static void my_free(void *p){
free(*(my_type **)p);
} 상황 2: 저 는 메모리 하 나 를 신 청 했 습 니 다. 이 메모리 의 내용 을 HashTable 에 추가 하려 면 코드 가 이렇게 보 입 니 다.
my_type *p;
p = malloc(sizeof(my_type));
zend_hash_add(ht, "key", sizeof("key"), (void *)p, sizeof(my_type), NULL); 이 럴 때 PHP 역시 먼저 Bucket 에 sizeof (Bucket) + 4 의 공간 을 신청 한 다음 add 가 들 어 온 데이터 가 포인터 가 아니 기 때문에 PHP 는 자체 적 으로 sizeof (my type) 크기 의 공간 을 신청 한 다음 p 가 가리 키 는 주소 의 메모리 copy 를 넣 습 니 다.
요 소 를 삭제 할 때 PHP 역시 석조 함 수 를 먼저 호출 한 다음 pData 의 공간 을 방출 하고 마지막 으로 Bucket 의 공간 을 방출 합 니 다.이러한 상황 에 대해 PHP 가 스스로 데 이 터 를 신청 / 방출 하 는 공간 을 알 수 있 습 니 다. 그러면 우 리 는 데 이 터 를 HashTable 에 추가 한 후에 그것 을 방출 할 수 있 습 니 다. 그러나 더 좋 은 방법 은 스 택 메모 리 를 사용 하 는 것 입 니 다.
my_type p;
/* do some assignment here */
……
zend_hash_add(ht, "key", sizeof("key"), (void *)&p, sizeof(p), NULL); 이렇게 하면 스스로 석조 함 수 를 정의 할 필요 가 없어 서 매우 편리 하 다.
두 번 째 상황 이 이렇게 편리 해 보이 는데 왜 첫 번 째 가 필요 하 냐 고 물 어 볼 수도 있 습 니 다.우 리 는 이러한 상황 을 상상 할 수 있다. 우리 가 삽입 해 야 할 데 이 터 는 다른 구조 체 의 지침 에서 나온다. 우리 가 HashTable 을 통 해 데 이 터 를 수정 한 후에 다른 구조 체 를 통 해 이 데 이 터 를 방문 하고 자 할 때 데이터 도 실시 간 으로 업데이트 된다.이렇게 하면 데 이 터 를 복사 할 수 없고 지침 만 전달 할 수 있다.또한 HashTable 이라는 구조 분석 을 할 때 데이터 가 다른 구조 체 를 통 해 접근 할 수 있 도록 해 야 합 니 다. 인용 수 를 유지 하고 분석 함수 에서 이 인용 계수 의 크기 를 판단 하여 데이터 의 메모 리 를 방출 할 지 여 부 를 결정 해 야 합 니 다.
메모리 방출 과 관련 된 두 가지 함수 가 있 습 니 다.
ZEND_API void zend_hash_destroy(HashTable *ht);
ZEND_API void zend_hash_clean(HashTable *ht); 이 두 함 수 는 모든 요소 의 분석 함 수 를 순서대로 호출 하여 모든 bucket 의 공간 을 청소 합 니 다.그들의 차 이 는 'zend' 이다.hash_destroy 는 zend 를 제거 합 니 다.hash_init 가 신청 한 bucket 포인터 목록 의 공간, zendhash_clean 은 포인터 만 0 으로 설정 합 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
java에서 Hashtable과 HashMap의 차이점 분석1. Hashtable은 Dictionary의 하위 클래스입니다 HashMap: HashMap과 Hashtable은 모두 맵 인터페이스의 실현 클래스이다. 2. Hashtable의 방법은 동기화()이고 HashMap...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.