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:
char id[8];
- memory for 8 characters is allocated in the stack and the address is assigned toid
.snprintf(id, 8, "entry-%d", i);
- the pointer to theid
buffer is passed tosnprintf
which fills it with the characters of the created string.struct Entry *e = new_entry(id);
- the pointer to theid
buffer is passed tonew_entry
where a new Entry struct is allocated on the heap and theid
(pointer) is assigned to itsid
member.bucket_add(b, e);
- the pointer to a bucket and the pointer to the entry are passed in tobucket_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:
- In each iteration of the loop, you're creating a new
char
array namedid
with space for 8 characters. - You then fill this array with the formatted string using
snprintf
. - However, when you pass
id
tonew_entry
, you're passing the pointer to this array, not the contents of the array itself. - The
new_entry
function then assigns this pointer directly to theid
field of theEntry
struct, so all entries end up pointing to the same memory location whereid
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.