【发布时间】:2021-03-20 05:42:56
【问题描述】:
我想我对 C 中的内存管理约定有点困惑。
假设我们有一个在堆上动态分配数据的结构。
这个结构体提供了_alloc() 和_free() 函数,用于在堆上分配和释放这个结构体。
这是一个简单的向量结构示例:
struct vec_t {
int *items;
size_t size;
size_t capacity;
};
struct vec_t *vec_alloc(void)
{
struct vec_t *vec = malloc(sizeof(struct vec_t));
vec->items = NULL;
vec->size = 0;
vec->capacity = 0;
return vec;
}
void vec_free(struct vec_t *vec)
{
free(vec->items);
free(vec);
}
void vec_push(struct vec_t *vec, int item)
{
if (vec->size == vec->capacity)
{
size_t new_capacity = vec->capacity > 0 ? (vec->capacity + 1) * 2 : 5;
vec->items = realloc(vec->items, new_capacity * sizeof(int));
}
vec->items[vec->size++] = item;
}
现在假设我们不使用_alloc() 或_free(),而是决定在堆栈上分配这个结构。
然后我们通过栈分配结构(my_vec.items)间接在堆上分配数据
void main()
{
vec_t my_vec;
vec_push(&my_vec, 8); // allocates memory
// vec_destroy(&my_vec); // PROBLEM
return 0;
}
现在我们有一个问题:我们不想释放结构体 (my_vec),因为它在堆栈上,但我们需要释放结构体在堆上分配的数据 (my_vec.items)。
我认为这要么是设计问题,要么是约定问题,或者两者兼而有之。
我看到人们在_alloc() 和_free() 之外添加了一些额外的功能_init() 和_deinit()。
void vec_init(struct vec_t *vec)
{
vec->items = NULL;
vec->size = 0;
vec->capacity = 0;
}
void vec_deinit(struct vec_t *vec)
{
free(vec->items);
}
释放_deinit()中的结构分配的内存有意义吗?
如果这种方法是正确的,我是否正确地说分配在这样的堆栈上的结构总是需要_init() 和_deinit()?
【问题讨论】:
-
通常的解决方案是通过将
struct vec_t设为不完整类型来禁止堆栈分配。如果你想允许堆栈分配,那么是的,你需要某种_init和_deinit函数来准备和清理。 -
@neeh _init 和 _deinit 是什么?
-
vec->items = realloc(vec, ...)是错误的。此外,你永远不应该重新分配你传递给realloc的指针。 -
您需要某种形式的 init 函数,否则您的堆示例使用未初始化的
my_vec对象,当您尝试使用它时可能会发生任何事情。 -
旁注:
vec_push(my_vec, 8);对于堆栈分配的情况是错误的;你大概是指vec_push(&my_vec, 8);那里are ways to make it work either way 使用单元素数组的typedef(这就是GMP 的工作原理),但在许多情况下它是不受欢迎的;openssl最终将其BIGNUMAPI 切换到不透明(强制堆)结构,部分原因是 API 处理堆栈或堆的复杂性。
标签: c memory memory-management