【问题标题】:How to store data in a dynamic array of structs?如何将数据存储在结构的动态数组中?
【发布时间】:2017-06-02 12:27:20
【问题描述】:

我有这些结构,我想用它来实现地图

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 设置数据后,一切似乎都存储在正确的位置。

任何人知道这些条目可以在哪里被覆盖或为什么不存储这些值?

【问题讨论】:

标签: c arrays struct dynamic-allocation


【解决方案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 来存储字符串的副本 - 并在最后释放它们。顺便说一句,由于您有一个初始化函数来初始化 Map,因此您应该构建一个清理函数,以便在一个地方释放所有必要的资源。

【讨论】:

    【解决方案2】:

    问题是map_put 中的变量entry 不是指针。它是一个结构。所以代码

    entry = map->entries[map->guard++];
    entry.name = name;
    entry.number = nr;
    

    map-&gt;entries[map-&gt;guard] 的内容复制到entry。然后更新entry 中的字段并从函数返回。

    正确的代码如下所示

    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;
    }
    

    【讨论】:

      【解决方案3】:

      问题是这样的:

      entry = map->entries[map->guard++];
      

      在此复制数组中的数据到entry 结构实例中。然后修改entry 的数据并丢弃这些修改。数组中的(原始)结构数据仍未修改。

      当您在下次调用map_put 时使用数组中未初始化的结构时,这当然会导致未定义的行为

      要么直接修改数组结构实例,单独增加map-&gt;guard。或者使entry成为一个指针并使其指向数组元素。

      【讨论】:

        猜你喜欢
        • 2011-11-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-22
        • 2013-02-22
        • 1970-01-01
        相关资源
        最近更新 更多