0

I'm making a HashMap in C but am having trouble detecting when a Node has been initialized or not.

Excerpts from my code below:

static struct Node
{
    void *key, *value;
    struct Node *next;
};


struct Node **table;
int capacity = 4;
table = malloc(capacity * sizeof(struct Node));

// At this point I should have a pointer to an empty Node array of size 4.

if (table[0] != NULL)
{
    // This passes
}

I don't see what I can do here. I've read tons of other posts of this nature and none of their solutions make any sense to me.

enter image description here

Hatefiend
  • 3,416
  • 6
  • 33
  • 74
  • Memory allocated with `malloc` is not zero-initialized. You can use [`memset`](http://man7.org/linux/man-pages/man3/memset.3.html) to do that. `struct Node **table;` makes a "list" of pointers, but you allocated a "list" of `Node`s. – Tim Jul 17 '16 at 03:56

2 Answers2

1

malloc does not initialize the memory allocated. You can use calloc to zero-initialize the memory.

// Not sizeof(struct Node)
// table = calloc(capacity, sizeof(struct Node));
table = calloc(capacity, sizeof(*table));

After that, it will make sense to use:

if (table[0] != NULL)
{
   ...
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
-1

I suggest you consider something like a HashMapCollection type that you create with a set of functions to handle the various memory operations you need.

So you might have code something like the following. I have not tested this nor even compiled it however it is a starting place.

The FreeHashMapCollection() function below would process a HashMapCollection to free up what it contains before freeing up the management data structure. This may not be what you want to do so that is something for you to consider.

The idea of the following is to have a single pointer for the HashMapCollection struct and the array or list of HashMapNode structs immediately follows the management data so a single free() would free up everything at once.

typedef struct _TAGHashMapNode {
    void *key, *value;
    struct _TAGHashMapNode  *next;
} HashMapNode;

typedef struct {
    int  iCapacity;   // max number of items
    int  iSize;       // current number of items
    HashMapNode *table;  // pointer to the HashMapNode table
} HashMapCollection;

Then have a function to allocate a HashMapCollection of a particular capacity initialized properly.

HashMapCollection *AllocateHashMapCollection (int iCapacity)
{
    HashMapCollection *p = malloc (sizeof(HashMapCollection) + iCapacity * sizeof(HashMapNode));

    if (p) {
        p->table = (HashMapNode *)(p + 1);
        p->iCapacity = iCapacity;
        p->iSize = 0;
        memset (p->table, 0, sizeof(HashMapNode) * iCapacity);
    }
    return p;
}

HashMapCollection *ReallocHashMapCollection (HashMapCollection *p, int iNewCapacity)
{
    HashMapCollection *pNew = realloc (p, sizeof(HashMapCollection) + sizeof(HashMapNode) * iNewCapacity);

    if (pNew) {
        pNew->table = (HashMapNode *)(pNew + 1);
        if (p == NULL) {
          // if p is not NULL then pNew will have a copy of that.
          // if p is NULL then this is basically a malloc() so initialize pNew data.
          pNew->iCapacity = pNew->iSize = 0;
        }
        if (iNewCapacity > pNew->iCapacity) {
            // added more memory so need to zero out that memory.
            memset (pNew->table + iCapacity, 0, sizeof(HashMapNode) * (iNewCapacity - pNew->iCapacity));
        }
        pNew->iCapacity = iNewCapacity;    // set our new current capacity
        p = pNew;   // lets return our new memory allocated.
    }
    return p;    // return either old pointer if realloc() failed or new pointer
}

void FreeHashMapCollection (HashMapCollection *p)
{
    // go through the list of HashMapNode items and free up each pair then
    // free up the HashMapCollection itself.

    for (iIndex = 0; iIndex < p->iCapacity; iIndex++) {
        if (p->table[iIndex].key) free (p->table[iIndex].key);
        if (p->table[iIndex].value) free (p->table[iIndex].value);
        // WARNING ***
        // if these next pointers are actually pointers inside the array of HashMapNode items
        // then you would not do this free as it is unnecessary.
        // this free is only necessary if next points to some memory area
        // other than the HashMapNode table of HashMapCollection.
        if (p->table[iIndex].next) free (p->table[iIndex].next);
        // even though we are going to free this, init to NULL
        p->table[iIndex].key = NULL;
        p->table[iIndex].value = NULL;
        p->table[iIndex].next = NULL;
    }
    free (p);    // free up the memory of the HashMapCollection
}
Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
  • Wow thanks for this awesome post. Actually your code looks extremely similar to the one [I'm in the process of writing here](https://gist.github.com/anonymous/9970a8087a15f07f2762a2a09ea6ed94). One thing I notice is your `FreeHashMapCollection` method. Why do you need to free up memory and when? – Hatefiend Jul 17 '16 at 04:43
  • @Hatefiend I added the FreeHashMapCollection function as a way to free up the memory used more easily for instance if you are processing a particular data set then are done so you free up the HashMapCollection so that you can then start with a new one with a new data set. You may or may not need it. – Richard Chambers Jul 17 '16 at 04:49
  • One more question. The hashcode function I found online was said to be really good, but only takes in an `unsigned char` pointer. If I pass an integer into it, it crashes my program. Does this mean that if I get an `int` passed into my `put` method, then the `int % capacity` should be the index where I place the `Node`? Or should I get a seperate `hashcode` method for every possible type, including `double`? I'm used to java where it has a `hashCode()` method that turns ANYTHING into a hash. – Hatefiend Jul 17 '16 at 04:58
  • @Hatefiend I suspect that the hash code function you are talking about is a function that takes a character string and creates a hash by processing the string to generate a reasonably unique number within a specified range. The idea is that different text strings will always result in different hash values but the same text string will always result in the same hash value. Hash codes can be compared to determine if two strings are equal or not. It makes lookup faster. Probably what you would need to do is to convert the int to a text string then process it with your hash function. – Richard Chambers Jul 17 '16 at 07:21
  • Check out http://c.learncodethehardway.org/book/ex37.html as well as http://stackoverflow.com/questions/838404/implementing-a-hashmap or http://elliottback.com/wp/hashmap-implementation-in-c/ or https://github.com/petewarden/c_hashmap – Richard Chambers Jul 17 '16 at 07:24