PHP 의 HashTable 구현

20988 단어 Hashtable
다음으로 전송: http://it.taocms.org/07/1145.htm
 
데이터 구조:
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;

초기 화 과정 은 다음 과 같이 요약 할 수 있 습 니 다.
  • zval 의 value. ht 신청 sizeof (HashTable) 크기 의 메모리
  • 호출zend_hash_init 함수 초기 화
  • zval 의 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 에서 의 "Zend HashTable 상세 설명")
    우리 가 조작 하고 자 하 는 것 이 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 으로 설정 합 니 다.

    좋은 웹페이지 즐겨찾기