C 错误中的列表 - 变量“内存”周围的堆栈已损坏

问题描述 投票:0回答:1

我正在尝试在 C 中实现一个动态列表,但我不明白为什么会出现“变量‘内存’周围的堆栈已损坏”。当我尝试向列表中添加第二个项目时出错。

struct LIST {
    unsigned int size;
    unsigned int count;
    void** items;
};

struct LIST* list_init(unsigned int size)
{
    struct LIST* createList = (struct LIST*)calloc(1, sizeof(struct LIST));
    void* memory = calloc(size, sizeof(void*));
    if (memory == 0 || createList == 0) return 0;

    createList->items = &memory;
    createList->size = size;

    return createList;
}

void list_add(struct LIST* list, void* item)
{
    if (list->count + 1 > list->size)
    {
        void* memory = realloc(list->items, (sizeof(void*)) * list->count + 1);
        list->items = &memory;
        return;
    }
    else
    {
        list->items[list->count] = item;
        list->count = list->count + 1;
    }
}

int main()
{
    const char* str = "Hello";
    struct LIST* list = list_init(2);

    list_add(list, (void*)str);
    list_add(list, (void*)str);
    list_add(list, (void*)str);
    list_add(list, (void*)str);

    printf("Items in List: %d \n\n", list_count(list));

    list_clear(list);

    free(list);
    return 0;
}

代码永远不会进入可以对内存变量做任何事情的 if 块,但是在第二个 list_add 调用中它在退出函数时崩溃(注意我正在使用 VS2022 调试器逐步执行它)。

我希望在使用 realloc 将列表扩展一个之前将 2 个项目添加到列表中。

c list realloc calloc
1个回答
0
投票

几个问题...

  1. LIST
    中,
    items
    应该只是
    void *
    not
    void **
  2. list_add
    中,
    memory
    是超出作用域的栈局部变量。将
    list->items = &memory
    更改为
    list->items = memory
  3. 您只需在
    list->count + 1
    中使用
    realloc
    。我们想增加
    list->size
    并在调用中使用它。
  4. 应无条件执行
    实际商店代码(在
    else块中)
  5. 存储代码试图取消引用一个
    void *
    指针
  6. 因为列表存储的是strings(即
    char *
    数组),所以我们在存储的时候需要复制字符串。所以,我们应该使用
    strdup
  7. 因为新的
    strdup
    ,最后只做
    free(list)
    是不够的。我们需要遍历所有元素并释放我们在
    list_add
    .
  8. 中复制的字符串
  9. list_init
    正在复制
    list_add
    中的代码。它应该just只做first
    calloc
  10. 因为我们可以将字符串文字传递给
    list_add
    (例如
    list_add(list,"Hello");
    ,第二个arg应该有
    const

这里是重构代码。它用错误和修复注释:

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

struct LIST {
    unsigned int size;
    unsigned int count;
#if 0
    void **items;
#else
    void *items;
#endif
};

struct LIST *
list_init(unsigned int size)
{
#if 0
// NOTE/BUG: do _not cast the return of calloc (or malloc/realloc)
    struct LIST *createList = (struct LIST *) calloc(1, sizeof(struct LIST));
#else
    struct LIST *createList = calloc(1, sizeof(*createList));
#endif

#if 0
// NOTE/BUG: this replicates code in list_add
    void *memory = calloc(size, sizeof(void *));

    if (memory == 0 || createList == 0)
        return 0;

    createList->items = &memory;
    createList->size = size;
#endif

    return createList;
}

#if 0
// NOTE/BUG: this should be a const pointer because we can pass in a string
// literal
void
list_add(struct LIST *list, void *item)
#else
void
list_add(struct LIST *list, const void *item)
#endif
{

    if (list->count + 1 > list->size) {
#if 0
// NOTE/BUG: we need to increment list->size
        void *memory = realloc(list->items, (sizeof(void *)) * list->count + 1);
#else
        list->size += 3;
        void *memory = realloc(list->items, (sizeof(void *)) * list->size);
#endif

#if 0
// NOTE/BUG: the variable 'memory' is on the stack and goes out of scope
        list->items = &memory;
        return;
#else
        list->items = memory;
#endif
    }

#if 0
// NOTE/BUG: this should be executed unconditionally
// NOTE/BUG: we need to duplicate the 'item' string
// NOTE/BUG: we can _not_ dereference a void * pointer
    else {
        list->items[list->count] = item;
        list->count = list->count + 1;
    }
#else
    char **items = list->items;
    items[list->count] = strdup(item);
    list->count = list->count + 1;
#endif
}

#if 1
void
list_print(struct LIST *list)
{

    printf("Items in List: %d\n", list->count);

    const char **items = list->items;
    if (items != NULL) {
        for (unsigned int idx = 0;  idx < list->count;  ++idx, ++items)
            printf("Item: %s\n",*items);
    }
}

void
list_free(struct LIST *list)
{

    char **items = list->items;

    if (items != NULL) {
        for (unsigned int idx = 0;  idx < list->count;  ++idx, ++items)
            free(*items);
    }

    free(list);
}

#endif

int
main(void)
{
    const char *str = "Hello";
    struct LIST *list = list_init(2);

#if 0
// NOTE/BUG: no need to cast str and can be detrimental as in masks the
// const nature of a string literal as below
    list_add(list, (void *) str);
    list_add(list, (void *) str);
    list_add(list, (void *) str);
    list_add(list, (void *) str);
#else
    list_add(list, str);
    list_add(list, "abc");
    list_add(list, "def");
    list_add(list, "ghi");
    list_add(list, "jkl");
#endif

#if 0
    printf("Items in List: %d \n\n", list_count(list));

    list_clear(list);
#else
    list_print(list);
#endif

#if 0
    free(list);
#else
    list_free(list);
#endif

    return 0;
}

在上面的代码中,我使用了

cpp
条件来表示旧代码与新代码:

#if 0
// old code
#else
// new code
#endif

#if 1
// new code
#endif

注意:这可以通过运行文件来清理

unifdef -k


程序输出如下:

Items in List: 5
Item: Hello
Item: abc
Item: def
Item: ghi
Item: jkl

这是清理后的代码:

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

struct LIST {
    unsigned int size;
    unsigned int count;
    void *items;
};

struct LIST *
list_init(unsigned int size)
{
    struct LIST *createList = calloc(1, sizeof(*createList));

    return createList;
}

void
list_add(struct LIST *list, const void *item)
{

    if (list->count + 1 > list->size) {
        list->size += 3;
        void *memory = realloc(list->items, (sizeof(void *)) * list->size);

        list->items = memory;
    }

    char **items = list->items;
    items[list->count] = strdup(item);
    list->count = list->count + 1;
}

void
list_print(struct LIST *list)
{

    printf("Items in List: %d\n", list->count);

    const char **items = list->items;
    if (items != NULL) {
        for (unsigned int idx = 0;  idx < list->count;  ++idx, ++items)
            printf("Item: %s\n",*items);
    }
}

void
list_free(struct LIST *list)
{

    char **items = list->items;

    if (items != NULL) {
        for (unsigned int idx = 0;  idx < list->count;  ++idx, ++items)
            free(*items);
    }

    free(list);
}

int
main(void)
{
    const char *str = "Hello";
    struct LIST *list = list_init(2);

    list_add(list, str);
    list_add(list, "abc");
    list_add(list, "def");
    list_add(list, "ghi");
    list_add(list, "jkl");

    list_print(list);

    list_free(list);

    return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.