C - 在函数内部分配内存时,谁负责内存分配?

C - Who is responsible for memory allocation when memory is allocated inside a function?

我正在尝试提升我的 C 级游戏。我目前正在使用 Kyle Loudon 的书“Mastering Algorithms with C”。目前我正在研究数据结构,特别是堆栈。我将在此处使用的代码示例并非直接来自本书,而是为了便于阅读而稍作修改。我在网上看过,但没有找到任何问题的答案。

我在 C 编程中经常听到的一条常见规则是“分配内存的人负责释放它”。这似乎是一个简单的规则。但是,当我尝试创建一个函数来将新的堆栈元素压入堆栈顶部时,我有点不确定。

密码

StackElmt 是驻留在堆栈中的元素。在堆栈上执行任何操作之前,stack_init 需要是 运行。之后 stack_push 将新元素压入栈顶,stack_pop 将它们从栈中弹出。

typedef struct StackElmt_ {
    void *data;
    struct StackElmt_ *next;
}StackElmt;

typedef struct Stack_
{
    int size;
    StackElmt *top;
}Stack;

void stack_init(Stack *stack)
{
    stack->size = 0;
    stack->top = NULL;
}

void stack_push(Stack *stack, void *data)
{

    StackElmt *element = calloc(1, sizeof(StackElmt));
    element->data = data;
    element->next = stack->top;
    stack->top = element;
    stack->size++;
}

void stack_pop(Stack *stack, StackElmt **element)
{
    *element = stack->top;
    stack->top = (*element)->next;
    stack->size--;
}

问题描述

我觉得很奇怪 stack_push 将内存分配给堆但 stack_pop 没有释放它。我觉得如果 stack_push 分配内存,内存管理就变成了这些 Stack 函数的开发者的责任,而不是用户的责任。

相反,我想做这样的事情:

void stack_push(Stack *stack, StackElmt *element, void *data)
{
    element->data = data;
    element->next = stack->top;
    stack->top = element;
    stack->size++;
}

输入指向 StackElmt 的指针,用户可以自行选择如何分配它。

总结

我的问题是:

  1. 在 stack_push() 中分配内存但在 stack_pop() 中未释放内存的代码是否被认为是好的设计?在 stack_pop() 中明确评论用户负责 StackElmt 内存是否会使设计更好?
  2. 书上还有一个叫做“destroy_stack”的函数,就是破坏栈的。其中,“stack_pop”和一个指针指向的自定义函数(建议免费)对于Stack中的每个元素都是运行。从负责内存管理的用户角度来看,这个“销毁”功能是否更清楚?
  3. 内存分配完全由用户分配(见我修改的stack_push)还是由程序完成更好?

提前谢谢大家!

stack_pop() 不应该 return StackElmt,它应该 return 元素中的 data。这反映了 stack_push().

的动作

然后它可以释放元素。

void stack_pop(Stack *stack, void **data)
{
    if (!stack->top) {
        // report stack underflow error somehow
        return;
    }
    *data = stack->top->data;
    StackElmt *temp = stack->top;
    stack->top = temp->next;
    stack->size--;
    free(temp);
}

函数 stack_pop 不应公开节点类型,而是 return 数据。使用这种方法,可以在函数中释放节点。我们还应该确保在弹出元素之前堆栈不为空。这是修改后的版本:

void stack_pop(Stack *stack, void **data)
{
    StackElmt *oldTop;

    assert(stack != NULL);
    assert(stack->size > 0);

    *data = stack->top->data;
    oldTop = stack->top;
    stack->top = stack->top->next;
    free(oldTop);
    stack->size--;
}

您可能还想定义一个 stack_size 函数,以便客户端可以在调用 [=21 之前检查堆栈是否为空=].