Как исправить копию структуры в массиве структур segfault - PullRequest
0 голосов
/ 30 сентября 2018

У меня есть две следующие структуры:

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

Как это сделать правильно?

Код приведен ниже (см. // *получить segfault здесь !!! как исправить * комментарий)

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

1 Ответ

0 голосов
/ 30 сентября 2018

Когда вы делаете

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

, содержимое новой выделенной памяти будет неопределенным , оно не инициализируется.Это означает, что в следующей строке

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

вы будете разыменовывать неверный указатель kvs->array[kvs->length].Это, конечно, приведет к неопределенному поведению .

Конечно, решение состоит в том, чтобы сделать kvs->array[kvs->length] точку где-то действительной, например, выполнив

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