为什么结构的 free() 会导致段错误(指针的错误使用)?

Why does free() of a struct result in segfault (wrong usage of pointers)?

当我尝试释放我的结构时,程序因段错误而崩溃。用 valgrind 检查程序我发现:

==9761== Invalid free() / delete / delete[] / realloc()
==9761==    at 0x484827F: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9761==    by 0x109242: destroyHashTable (hashtable.c:38)
==9761==    by 0x10942E: main (hashtable_main.c:17)
==9761==  Address 0x1ffefffa70 is on thread 1's stack
==9761==  in frame #2, created by main (hashtable_main.c:7)

除了不知道如何解决它,我真的没有什么比这更有用的了。崩溃发生在 hashtable.cdestroyHashTable(ht)free(ht) 期间。我做错了什么?

代码下方hashTable_main.c:

#include <stdio.h>
#include <stdlib.h>


#include "hashtable.h"

int main() {

    hashTable* ht = NULL;

    initHashTable(&ht);

    int totalColCount = 0;

    totalColCount += addHashTableEntry(&ht, "PRPR2");

    destroyHashTable(&ht);

    return EXIT_SUCCESS;
}

hashtable.c:

#include <stdlib.h>
#include <stdio.h>


#include "hashtable.h"

/* private internal API */
int hash_funktion(char *string);
hashtableEntry* createTableEntry(char* newKey) ;
/* end of private internal API */


int hash_funktion(char *string) {
    unsigned int hash_adresse;
    unsigned char *pointer;
    hash_adresse = 0;
    pointer = (unsigned char *) string;
    while(*pointer != '[=12=]') {
        hash_adresse = 19 * hash_adresse + *pointer;
        pointer++;
    }
    return hash_adresse % MAX_HASH;
}

hashtableEntry* createTableEntry(char* newKey) {
     hashtableEntry* e = (hashtableEntry*) malloc (sizeof(hashtableEntry));
     e->hashKey = newKey;
     return e;
}

void initHashTable(hashTable* ht) {
    ht = (hashTable*) malloc (sizeof (struct hashTable));
    ht->table = (hashtableEntry*) malloc (MAX_HASH * sizeof (hashtableEntry));
}

void destroyHashTable(hashTable* ht) {
    if (ht) {
        free(ht);
        ht = NULL;
    }
}

int  addHashTableEntry(hashtableEntry* ht, char* keyValue) {
    hashtableEntry *e = createTableEntry(keyValue);

    int colCounter = 0;

    int hashValue = hash_funktion(keyValue);

    if (ht[hashValue].hashKey == NULL) {
        ht[hashValue] = *e;
        return 0;
    } else {
        int newVal = (hashValue + 1) % MAX_HASH;
        colCounter++;
        while (ht[newVal].hashKey != NULL && newVal != hashValue ) {
            newVal = (newVal + 1) % MAX_HASH;
            colCounter++;
        }
        if (newVal != hashValue) {
            ht[newVal] = *e;  
            return colCounter;      
        } else {
            return -1;
        }
    }
}

bool searchValue(hashtableEntry* ht, char* searchValue) {    
    for (int i = 0; i < MAX_HASH; i++)
    {
        if(ht[i].hashKey == searchValue) {
            return true;
        }
    }
    return false;
}

hashtable.h:

#pragma once

#define MAX_HASH 20
#include <stdbool.h>

typedef struct hashtableEntry {
    char* hashKey;
} hashtableEntry;

typedef struct hashTable {
    hashtableEntry* table;
    int elemCount;
} hashTable;

void initHashTable(hashTable* ht);

void destroyHashTable(hashTable* ht);

int  addHashTableEntry(hashtableEntry* ht, char* keyValue);

bool searchValue(hashtableEntry* ht, char* searchValue);

从来没有散列table 开头。问题出在initHashTable。它应该接受一个双指针,因为它被赋予了一个指向它应该初始化的指针的指针。尽管在 destroyHashTable 中进行了检查,但它仍会出现段错误的原因是指针未初始化,并且在程序执行开始时可能为 non-zero。

void initHashTable(hashTable** ht) {
    *ht = (hashTable*) malloc (sizeof (struct hashTable));
    (*ht)->table = (hashtableEntry*) malloc (MAX_HASH * sizeof (hashtableEntry));
}

您可能会发现 return 新创建的散列 table 更容易。这更好地表达了 initHashTable 给你一个新的 hashTable * 值。

hashTable *initHashTable() {
    hashTable *ht = (hashTable *) malloc (sizeof (struct hashTable));
    ht.table = (hashtableEntry *) malloc (MAX_HASH * sizeof (hashtableEntry));
    return ht;
}

还有很多地方没有正确处理指针。

void doThing(Foo *foo) {
    // This changes foo, but not the data foo points to.
    foo = something;
    // This changes the data foo points to
    *foo = someOtherThing;
}

void doStuff() {
    Foo *foo;

    // This is incorrect since it creates a double pointer. doThing would need to
    // be defined as "void doThing(Foo **foo)" to be correct.
    doThing(&foo);

    // Instead we can just pass the existing pointer
    doThing(foo);


    // We only need to create a reference if the value does not start out as a pointer
    Foo bar;
    doThing(&bar);
}