我正在尝试在 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 个项目添加到列表中。
几个问题...
LIST
中,items
应该只是void *
和notvoid **
list_add
中,memory
是超出作用域的栈局部变量。将list->items = &memory
更改为list->items = memory
list->count + 1
中使用realloc
。我们想增加 list->size
并在调用中使用它。实际商店代码(在
else
块中)void *
指针char *
数组),所以我们在存储的时候需要复制字符串。所以,我们应该使用strdup
strdup
,最后只做free(list)
是不够的。我们需要遍历所有元素并释放我们在 list_add
.list_init
正在复制list_add
中的代码。它应该just只做firstcalloc
。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;
}