c# C中的列表错误-变量“memory”周围的堆栈已损坏

dxxyhpgq  于 2023-04-10  发布在  C#
关注(0)|答案(1)|浏览(231)

我试图在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 Call中,它在退出函数时崩溃(注意,我使用VS2022调试器来单步调试它)。
我希望有2个项目添加到列表之前realloc是用来扩展列表的一个。

raogr8fs

raogr8fs1#

有几个问题...
1.在LIST中,items应该是void *,而不是void **
1.在list_add中,memory是超出作用域的堆栈局部变量。
1.您只需在realloc中使用list->count + 1。我们希望将list->size递增并在调用中使用它。
1.实际的存储代码(在else块中)应该无条件地执行
1.存储代码尝试取消引用void *指针
1.由于列表存储的是 * 字符串 *(即char *数组),因此在存储时需要复制字符串。因此,我们应该使用strdup
1.由于新的strdup,仅仅在最后执行free(list)是不够的。我们需要循环所有元素并释放在list_add中复制的字符串。

  1. list_init正在复制list_add中的代码。它应该只执行 * 第一个 * calloc
    1.因为我们 * 可以 * 传入字符串 * 文字 * 到list_add(例如list_add(list,"Hello");,第二个参数应该有const
    下面是重构后的代码。它带有bug和修复的注解:
#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;
}

相关问题