sourpet
sourpet

Reputation: 17

Hashtable Add - C

Getting some segfault on the following algorithm to add an element to the correct bucket in a hashtable.

My structures are basic:

struct kv {
    char* key;
    unsigned val;
    struct kv* next;
};

struct hashtable {
    struct kv** table;
    unsigned size;
}; 

And my buggy function:

    struct kv* ht_find_or_put(char* word, unsigned value,
                                  struct hashtablet* hashtable,
                                  unsigned (*hash)(char*))
    {
        unsigned index = hash(word) % hashtable->size;
        struct kv* ke = malloc(sizeof (struct kv));

        for (ke = hashtable->table[index]; ke != NULL; ke = ke->next)
        {
            if (strcmp(ke->key, word) == 0)
                return ke;
        }

        if (ke == NULL)
        {
            ke->key = word;
            ke->val = value;
            ke->next = hashtable->table[index];
            hashtable->table[index] = ke;
        }
   return ke;
}

I know I haven't added yet all the tests (if malloc failed and such) just trying to debug this particular problem...

I'm allocating my table as such:

struct hashtable* hashtable_malloc(unsigned size)
{
    struct hashtable *new_ht = malloc(sizeof(struct hashtable));
    new_ht->size = size;
    new_ht->table = malloc(sizeof(struct kv) * size);

    for(unsigned i = 0; i < size; i++)
        new_ht->table[i] = NULL;

    return new_ht;
}

Any sort of help will greatly be appreciated. I'm only starting to learn.

Upvotes: 0

Views: 254

Answers (1)

MByD
MByD

Reputation: 137382

The first issue is a memory leak, e.g. - you allocate memory using malloc, but than loses the reference to it, as you override the pointer:

// allocate memory
struct kv* ke = malloc(sizeof (struct kv));
//   lose the reference
//   VVVVVVVVVVV
for (ke = hashtable->table[index]; ke != NULL; ke = ke->next)

The second issue, which probably causes the segfault, is that you try to de-reference a null pointer:

if (ke == NULL)
{
    // ke is NULL, you can't de-reference it
    ke->key = word;
    ke->val = value;
    ke->next = hashtable->table[index];
    hashtable->table[index] = ke;
}

The solution will be, IMHO, to allocate and put the new element, only upon failure to find it:

struct kv* ht_find_or_put(char* word, unsigned value, struct hashtablet* hashtable, unsigned (*hash)(char*))
{
    unsigned index = hash(word) % hashtable->size;
    struct kv* ke;

    // first we try to find the node
    for (ke = hashtable->table[index]; ke != NULL; ke = ke->next)
    {
        if (strcmp(ke->key, word) == 0)
            return ke;
    }

    // didn't find it - lets create and put a new one.    
    if (ke == NULL)
    {
        ke = malloc(sizeof (struct kv));
        // later add a check if the allocation succeded...
        ke->key = word;
        ke->val = value;
        ke->next = hashtable->table[index];
        hashtable->table[index] = ke;
    }
    return ke;
}

Since I didn't want to introduce entirely new code, that would just confuse you, I made the minimal changes to the original code.

Upvotes: 1

Related Questions