【问题标题】:How to fix copy of struct to array of structs segfault如何将结构的副本修复为结构数组段错误
【发布时间】:2023-03-31 07:32:01
【问题描述】:

我有以下两个结构:

typedef struct {
    char* key;
    char* value;
} kvpair;

typedef struct {
    kvpair ** array;
    size_t length;
} kvarray;

我想将新的键值对复制到 kvarray。我使用 realloc 为要添加到 kvpair 数组中的每个新项目分配内存,但很难弄清楚如何复制键和值。

如果我这样做:

  kvs->array resized using realloc

    // *** get segfault here!!! how to fix ***
    kvs->array[kvs->length]->key = key;
  kvs->array[kvs->length]->value = value;

但如果我为 kvpair* 单独分配内存并这样做:

kvpair* kvp = malloc(sizeof(kvpair));
// copy key and value

// This below then works
kvs->array[kvs->length] = kvp;
// but there is a memory leak - or seems to be double allocation of memory for same thing

如何正确地做到这一点?

代码如下(参见 // * get segfault here!!! 如何修复 * 注释)

#include <stdlib.h>
#include <stdio.h>
#include <string.h>


typedef struct {
    char* key;
    char* value;
} kvpair;

typedef struct {
    kvpair ** array;
    size_t length;
} kvarray;

kvarray * readKVs(const char** array, size_t length);
void freeKVs(kvarray * pairs);

int main() {
    const char* things[] = { "wood=brown\n", "brick=red\n", 
        "grass=green", "hedge=green", "leaf=green" };
    const size_t sz = sizeof(things) / sizeof(things[0]);

    kvarray* kvs = readKVs(things, sz);
    freeKVs(kvs);
}

kvarray * readKVs(const char** array, size_t length) {

    kvarray* kvs = NULL;

    for (size_t i = 0; i < length; ++i) {
        const char* line = array[i];

        if (kvs == NULL) {
            kvs = malloc(sizeof(kvarray));
            kvs->length = 0;
            kvs->array = NULL;
        }

        char * found = strchr(line, '=');
        if (found == NULL) {
            // skip to next line
            continue;
        }

        size_t len = strlen(line);
        size_t pos = found - array[i];

        char* value = NULL;
        if (len > (pos + 1)) {
            // non-blank value
            // length of value is len - pos
            value = malloc(len - (pos + 1));
            strncpy(value, &line[pos + 1], (len - (pos + 1)) - 1);
            // null terminate string
            value[len - (pos + 1) - 1] = '\0';
            printf("value:'%s'\n", value);
        }

        char* key = malloc(found - line + 1);  // +1 for null terminator
        strncpy(key, line, pos);
        // remember strncpy bug!
        key[found - line] = '\0';  // ensure null termination. 
        printf("key:'%s', length=%lu\n", key, strlen(key));

        /*
        // if I allocate an individual pair, then I am duplicating memory so should have to do this below
        kvpair* kvp = malloc(sizeof(kvpair));
        //kvpair kvp = {NULL, NULL};
        printf("about to assign kvs->key = key\n");
        kvp->key = key;
        printf("about to assign kvs->value = value\n");
        kvp->value = value;
        */

        kvs->array = realloc(kvs->array, (kvs->length + 1) * sizeof(kvpair*));

        // I want to be able to do this 2 lines below - but crashes
        // *** get segfault here!!! how to fix ***
        kvs->array[kvs->length]->key = key;
        kvs->array[kvs->length]->value = value;

        kvs->length++;
        printf("kvs->length now=%lu\n", kvs->length);
    }
    return kvs;
}

void freeKVs(kvarray * pairs) {
    if (pairs == NULL) {
        return;
    }

    for (size_t i = 0; i < pairs->length; ++i) {
        free(pairs->array[i]->key);
        free(pairs->array[i]->value);
        free(pairs->array[i]);
    }
    free(pairs);
}

【问题讨论】:

  • OT:调用任何堆分配函数时 1) 始终检查 (!=NULL) 返回值以确保操作成功。 2) 调用realloc 时,始终将返回值分配给void *temp 变量,然后检查(!=NULL) 该临时变量,如果不为NULL,则分配给目标变量。否则当realloc失败时,指向原来分配内存的指针会被NULL覆盖,导致不可恢复的内存泄漏

标签: c memory-leaks segmentation-fault


【解决方案1】:

当你这样做时

kvs->array = realloc(kvs->array, (kvs->length + 1) * sizeof(kvpair*));

分配的新内存的内容将是不确定的,它没有被初始化。这意味着下一行

kvs->array[kvs->length]->key = key;

您将取消引用无效指针 kvs-&gt;array[kvs-&gt;length]。这当然会导致undefined behavior

解决方案当然是使kvs-&gt;array[kvs-&gt;length] 点在某处有效,例如通过这样做

kvs->array[kvs->length] = malloc(sizeof(kvpair));

【讨论】:

  • 然后在freeKVs中我需要添加free(kvs->array); ?
  • @AngusComber 每个malloc(或realloc)调用都需要匹配一个free调用。
  • 更准确地说,每个malloc()calloc() 都需要与free() 匹配。有一次,realloc() 需要与 free() 匹配,此时“旧空间”指针为 NULL;那么它的行为就像malloc()。您还应该减少对 realloc() 的调用,其中新大小为零并且返回的指针为 NULL,因为它释放了内存。 (换句话说,realloc() 调用并不总是需要它们自己的free() 调用,因为它们会更改由malloc()calloc() 进行的分配。saga 还有一些其他曲折(例如aligned_alloc() )。但总体思路是正确的。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-15
  • 1970-01-01
相关资源
最近更新 更多