Reputation: 2876
I have these structs with which I would like to implement a map
typedef struct {
const char *name;
int number;
} Entry;
typedef struct {
int available;
int guard;
Entry *entries;
} Map;
and code to work to initialise and put elements in it:
Map *map_init() {
Map *res = (Map *) malloc(sizeof(Map));
res->available = 4;
res->guard = 0;
res->entries = (Entry *) malloc(4 * sizeof(Entry));
return res;
}
int map_put(Map *map, const char *name, int nr) {
Entry entry;
int i = 0;
for (i = 0; i < map->guard; ++i) {
entry = map->entries[i];
printf("entry ( x , %u) at %p (%p)\n", entry.number, &entry, entry.name);
if (!strcmp(entry.name, name)) // Segmentation fault here
return 0;
}
entry = map->entries[map->guard++];
entry.name = name;
entry.number = nr;
printf("entry (%s, %u) at %p (%p)\n", entry.name, entry.number, &entry, entry.name);
return 1;
}
when I run my main method
int main(int argc, char **argv) {
printf("Initialising...\n");
Map *map = map_init();
printf("Putting...\n");
map_put(map, "test", 2);
map_put(map, "some", 1);
// ...
free(map->entries);
free(map);
return 0;
}
I get as output
Initialising...
Putting...
entry (test, 2) at 0x7fff50b32a90 (0x10f0cdf77)
entry ( x , 0) at 0x7fff50b32a90 (0x5000000000000000)
Segmentation fault: 11
from which I could derive that the segmentation fault is due to the fact that entry.name
does not point to a string anymore (also the number is lost, but this does not lead to unauthorised memory access). After I set the data in the first invocation of map_put
, everything seems to be stored in the correct places.
Anyone an idea where these entries could be overwritten or why the values are not stored?
Upvotes: 2
Views: 746
Reputation: 148910
You have a major problem in map_put
. You use a local Entry
, in which you copy entries from the map. But when you later assign value to the local copy, the original entries from the map are left unchanged.
So when you later try to compare the new name to the existing entry, you are comparing it to uninitialized values, which is Undefined Behaviour.
You should use an Entry *
instead:
int map_put(Map *map, const char *name, int nr) {
Entry *entry;
int i = 0;
for (i = 0; i < map->guard; ++i) {
entry = map->entries + i;
printf("entry ( x , %u) at %p (%p)\n", entry->number, entry, entry->name);
if (!strcmp(entry->name, name)) // Segmentation fault here
return 0;
}
entry = &map->entries[map->guard++];
entry->name = name;
entry->number = nr;
printf("entry (%s, %u) at %p (%p)\n", entry->name, entry->number, entry, entry->name);
return 1;
}
But that is not all. You just store the address of a string in name. It is fine in this example, because you are actually passing string litteral constants. But if you read strings from standard input or a file, the content of the buffer will be overwritten with each new value. As you only store the address, you will end with all entries pointing to same value: the last one.
IMHO you should comtemplate using strdup
to store copies of the string - and free them at the end. BTW, as you have an init function to initialize you Map
, you should build a cleanup one to have all necessary free in one single place.
Upvotes: 1
Reputation: 34829
The problem is that variable entry
in map_put
is not a pointer. It is a structure. So the code
entry = map->entries[map->guard++];
entry.name = name;
entry.number = nr;
copies the contents of map->entries[map->guard]
into entry
. Then you update the fields in entry
and return from the function.
The correct code looks like this
int map_put(Map *map, const char *name, int nr) {
Entry *entry; // <-- entry is a pointer
int i = 0;
for (i = 0; i < map->guard; ++i) {
entry = &map->entries[i];
printf("entry ( x , %u) at %p (%p)\n", entry->number, (void *)entry, (void *)entry->name);
if (!strcmp(entry->name, name))
return 0;
}
entry = &map->entries[map->guard++];
entry->name = name;
entry->number = nr;
printf("entry (%s, %u) at %p (%p)\n", entry->name, entry->number, (void *)entry, (void *)entry->name);
return 1;
}
Upvotes: 1
Reputation: 409176
The problem is this:
entry = map->entries[map->guard++];
Here you copy the data from the array into the entry
structure instance. Then you modify the data of entry
and discard those modifications. The (original) structure data in the array is still unmodified.
That will of course lead to undefined behavior when you in the next call to map_put
use the uninitialized structures in the array.
Either modify the array structure instance directly and increase map->guard
separately. Or make entry
a pointer and make it point to the array element.
Upvotes: 3