为什么我们在插入节点时在链表中使用malloc?

Why do we use malloc in linkedlist while inserting a node?

void push(struct Node** head_ref, int new_data)
{
    
    struct Node* new_node = (struct Node*) malloc(sizeof(struct Node)); // WHY this?
    new_node->data = new_data;
    new_node->next = (*head_ref);
    (*head_ref) = new_node;
}

这是关于对象的生命周期。

C 标准将此描述为“存储持续时间”。最常见的 3 种类型是

  1. 自动存储时长

  2. 分配的存储期限

  3. 静态存储时长

具有自动存储持续时间的对象的生命周期仅限于定义对象的块。例如,函数中定义的变量将仅存在于函数内部。所以如果你这样做了

void push(struct Node** head_ref, int new_data)
{
    
    struct Node new_node;         // new_node has automatic storage duration
                                  // so it only exist within this function
    new_node.data = new_data;
    new_node.next = (*head_ref);
    (*head_ref) = &new_node;      // Very bad... never do this
}

你最终会遇到 *head_ref 指向死对象的情况。

所以对于链表,你就是不能使用局部变量。

具有已分配存储持续时间的对象的生命周期从 malloc(或朋友)return 开始,直到您的代码通过调用 free 显式终止该对象。这正是您想要的链表。您可以控制其生命周期的对象。

注意:当你这样做时

struct Node* new_node = malloc(sizeof(struct Node));

游戏中有两个对象。 new_node 本身仍然是一个具有自动存储持续时间的对象(并且只会存在于函数内部)但是它的值(即由 malloc 编辑的值 return )是一个指向节点对象的指针具有分配的存储持续时间。因此,您可以将 new_node 的值保存在链表中,并有一个指向在函数 returns.

时仍然存在的对象的指针

静态存储对象的生命周期是从程序开始到程序结束。所以这样的对象可以用于链表。您可以编写一个带有静态节点池的程序,只要您需要链表中的新节点,就可以从该池中获取一个节点。这是可行的,但需要额外的代码来维护可用节点池(即实现您自己的 Node-malloc/Node-free 版本)。另一个缺点是它将列表的最大长度限制为固定数量(即启动时池中静态节点对象的数量)。

因此,使用分配了存储持续时间的对象是迄今为止最简单的方法。当您需要一个新节点时,只需调用 malloc。完成节点后,只需调用 free.

在某些特殊情况下,您可能会选择具有静态存储持续时间的对象池。