如何将数据存储在动态结构数组中?
How to store data in a dynamic array of structs?
我有这些结构,我想用它们来实现映射
typedef struct {
const char *name;
int number;
} Entry;
typedef struct {
int available;
int guard;
Entry *entries;
} Map;
以及初始化和放入元素的代码:
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;
}
当我运行我的主要方法
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;
}
我得到输出
Initialising...
Putting...
entry (test, 2) at 0x7fff50b32a90 (0x10f0cdf77)
entry ( x , 0) at 0x7fff50b32a90 (0x5000000000000000)
Segmentation fault: 11
从中我可以得出分段错误是由于 entry.name
不再指向字符串(数字也丢失了,但这不会导致未经授权的内存访问)。在我第一次调用 map_put
设置数据后,一切似乎都存储在正确的位置。
有人知道这些条目可以在哪里被覆盖或者为什么不存储这些值吗?
问题是这样的:
entry = map->entries[map->guard++];
在这里,您将数组中的数据复制到entry
结构实例中。然后您修改 entry
的数据并丢弃这些修改。数组中的(原始)结构数据仍未修改。
当您在下一次调用 map_put
时使用数组中未初始化的结构时,这当然会导致 未定义的行为。
要么直接修改数组结构实例,单独增加map->guard
。或者使 entry
成为 指针 并使其指向数组元素。
问题是 map_put
中的变量 entry
不是指针。它是一个结构。所以代码
entry = map->entries[map->guard++];
entry.name = name;
entry.number = nr;
将map->entries[map->guard]
的内容复制到entry
中。然后从函数中更新 entry
和 return 中的字段。
正确的代码如下所示
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;
}
您在 map_put
中遇到了重大问题。您使用本地 Entry
,其中您从地图中 复制 条目。但是当您稍后为本地副本赋值时,地图中的原始条目将保持不变。
因此,当您稍后尝试将新名称与现有条目进行比较时,您将其与未初始化的值进行比较,这就是未定义的行为。
您应该改用 Entry *
:
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;
}
但这还不是全部。您只需将字符串的地址存储在名称中。在这个例子中很好,因为你实际上是在传递字符串常量。但是,如果您从标准输入或文件中读取字符串,缓冲区的内容将被每个新值覆盖。由于您只存储地址,因此所有条目都指向相同的值:最后一个。
恕我直言,您应该考虑使用 strdup
来存储字符串的副本 - 并在最后释放它们。顺便说一句,因为你有一个 init 函数来初始化你 Map
,你应该构建一个清理函数,以便在一个地方释放所有必要的东西。
我有这些结构,我想用它们来实现映射
typedef struct {
const char *name;
int number;
} Entry;
typedef struct {
int available;
int guard;
Entry *entries;
} Map;
以及初始化和放入元素的代码:
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;
}
当我运行我的主要方法
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;
}
我得到输出
Initialising...
Putting...
entry (test, 2) at 0x7fff50b32a90 (0x10f0cdf77)
entry ( x , 0) at 0x7fff50b32a90 (0x5000000000000000)
Segmentation fault: 11
从中我可以得出分段错误是由于 entry.name
不再指向字符串(数字也丢失了,但这不会导致未经授权的内存访问)。在我第一次调用 map_put
设置数据后,一切似乎都存储在正确的位置。
有人知道这些条目可以在哪里被覆盖或者为什么不存储这些值吗?
问题是这样的:
entry = map->entries[map->guard++];
在这里,您将数组中的数据复制到entry
结构实例中。然后您修改 entry
的数据并丢弃这些修改。数组中的(原始)结构数据仍未修改。
当您在下一次调用 map_put
时使用数组中未初始化的结构时,这当然会导致 未定义的行为。
要么直接修改数组结构实例,单独增加map->guard
。或者使 entry
成为 指针 并使其指向数组元素。
问题是 map_put
中的变量 entry
不是指针。它是一个结构。所以代码
entry = map->entries[map->guard++];
entry.name = name;
entry.number = nr;
将map->entries[map->guard]
的内容复制到entry
中。然后从函数中更新 entry
和 return 中的字段。
正确的代码如下所示
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;
}
您在 map_put
中遇到了重大问题。您使用本地 Entry
,其中您从地图中 复制 条目。但是当您稍后为本地副本赋值时,地图中的原始条目将保持不变。
因此,当您稍后尝试将新名称与现有条目进行比较时,您将其与未初始化的值进行比较,这就是未定义的行为。
您应该改用 Entry *
:
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;
}
但这还不是全部。您只需将字符串的地址存储在名称中。在这个例子中很好,因为你实际上是在传递字符串常量。但是,如果您从标准输入或文件中读取字符串,缓冲区的内容将被每个新值覆盖。由于您只存储地址,因此所有条目都指向相同的值:最后一个。
恕我直言,您应该考虑使用 strdup
来存储字符串的副本 - 并在最后释放它们。顺便说一句,因为你有一个 init 函数来初始化你 Map
,你应该构建一个清理函数,以便在一个地方释放所有必要的东西。