How do I overwrite a string buffer without modifying earlier use

ghz 8months ago ⋅ 74 views

How do I overwrite a string buffer without modifying earlier uses of it

I've recently started learning C, and I'm trying to implement a hash map datastructure. I'm currently implementing my buckets as dynamically resizing array-backed lists. When testing my bucket_remove function I made an error in my test code that I'm struggling to understand.

Here is my new_entry function:

struct Entry *new_entry(char *id)
{
    struct Entry *e = (struct Entry *)malloc(sizeof(struct Entry));
    e->id = id;
    return e;
}

And here is my test_remove function:

static char *test_remove()
{
    struct Bucket *b = new_bucket();

    for (int i = 0; i < 5; i++)
    {
        char id[8];
        snprintf(id, 8, "entry-%d", i);
        struct Entry *e = new_entry(id);
        bucket_add(b, e);
    }

    printf("Entry 0 id: %s.\n", b->_entries[0]->id);
    printf("Entry 1 id: %s.\n", b->_entries[1]->id);

    // ... rest of code omitted for clarity
}

Inside the loop at each iteration, a new entry is created with the correct id, but the previous entries' ids are then updated to the most recent id. So, the console prints:

Entry 0 id: entry-4.
Entry 1 id: entry-4.

I believe this is related to the way I am passing the local id buffer to my new_entity function. Each entry must be receiving a pointer to the same memory. But I can't quite figure out why this is the case.

This is how I understand the code in the loop:

  1. char id[8]; - memory for 8 characters is allocated in the stack and the address is assigned to id.
  2. snprintf(id, 8, "entry-%d", i); - the pointer to the id buffer is passed to snprintf which fills it with the characters of the created string.
  3. struct Entry *e = new_entry(id); - the pointer to the id buffer is passed to new_entry where a new Entry struct is allocated on the heap and the id (pointer) is assigned to its id member.
  4. bucket_add(b, e); - the pointer to a bucket and the pointer to the entry are passed in to bucket_add and the entry (pointer) is added to the bucket's array of entries.

When a new iteration occurs, it looks like a new section of memory is allocated and its new memory address is assigned to a new variable in local scope. So how does the previous memory get overwritten?

I would appreciate if someone could clarify my understanding, please.

Answers

Your understanding is mostly correct, but there's a key point that's causing the unexpected behavior: the id variable is being reused in each iteration of the loop, leading to all Entry structs pointing to the same memory location for their id fields.

Here's what's happening:

  1. In each iteration of the loop, you're creating a new char array named id with space for 8 characters.
  2. You then fill this array with the formatted string using snprintf.
  3. However, when you pass id to new_entry, you're passing the pointer to this array, not the contents of the array itself.
  4. The new_entry function then assigns this pointer directly to the id field of the Entry struct, so all entries end up pointing to the same memory location where id resides.

To fix this, you need to ensure that each Entry struct has its own copy of the id string. One way to do this is by dynamically allocating memory for the id field in the new_entry function:

struct Entry *new_entry(char *id)
{
    struct Entry *e = (struct Entry *)malloc(sizeof(struct Entry));
    e->id = (char *)malloc(strlen(id) + 1); // Allocate memory for the id string
    strcpy(e->id, id); // Copy the id string into the allocated memory
    return e;
}

With this change, each Entry struct will have its own copy of the id string, and modifying one id will not affect the others. Just remember to free the dynamically allocated memory when you're done using the Entry structs.