【问题标题】:how to remove a node from a linked list如何从链表中删除一个节点
【发布时间】:2026-01-01 04:50:01
【问题描述】:

我想在两个节点之间执行二元运算,将结果存储在一个节点中并消除另一个节点。这是我写的:

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <math.h>

    struct n{
        double value;
        char op;
        struct n *next;
    };

    void delete(struct n *head);
    void add_item(struct n **ptr, double *data);

    int main(){
        struct n *head = NULL;
        double result;
        add_item(&head, 5);
        add_item(&head, 3);
        head->op = '*';
        result = (head->next)->value * head->value;
        (head->next)->value = result;
        delete(head);
        printf("%lf\n",head->value);
        free(head); 
        return 0;        
    }

    void add_item(struct n **ptr, double *data)
    {
            struct n *item = malloc(sizeof *item);

            item->value = *data;
            item->next = *ptr;
            item->op = '?';
            *ptr = item;
    }

    void delete(struct n *head)
    {
        struct n *temp;
        temp = head->next;
        head->next = temp->next;
        free(temp);
    }

在这个例子中,我有一个类似3 -&gt; 5 -&gt; NUll 的列表。我想得到这个15 -&gt; NUll。 当我尝试打印剩余节点的值时,我得到3 而不是15

【问题讨论】:

  • 您显示的代码接近minimal reproducible example,但还不够接近。 head 是什么?包含哪些头文件?
  • 并想一想当您执行(head-&gt;next)-&gt;value = result; 后跟delete(head) 会无条件删除列表中的second 节点时会发生什么。
  • 解决您的问题的方法是创建一个函数,从列表中删除(但不删除)“头”节点,然后返回它。然后你可以移除头部两次以获取它们的值,执行你想要的操作,并添加一个带有结果的新节点。将列表视为堆栈(带有推送和弹出操作)。
  • 您的“删除”不会改变 head 的值。它仍然指向您要删除的相同元素。
  • 很确定你的意思是void delete(struct n **head) {struct n *tmp; tmp=(*head)-&gt;next; free(*head) ;*head=tmp;}

标签: c struct linked-list singly-linked-list


【解决方案1】:

这两个函数都无效。

对于函数add_item,您不通过引用传递数据(通过引用传递数据也没有意义)。

    add_item(&head, 5);
    add_item(&head, 3);

所以函数应该像这样声明和定义

void add_item(struct n **ptr, double data)
{
        struct n *item = malloc(sizeof *item);

        item->value = data;
        item->next = *ptr;
        item->op = '?';
        *ptr = item;
}

您还必须通过引用将头节点传递给函数delete

void delete(struct n **head)
{
    if ( *head )
    {
        struct n *temp = *head;
        *head = ( *head )->next;
        free( temp );
    }
}

然后这样称呼它

delete( &head );

当我尝试打印剩余节点的值时,我得到 3 而不是 15

这是因为你删除了head之后的节点,而不是删除了head节点,虽然你在head之后的节点中写入了操作的结果。

(head->next)->value = result;

这是您的更新程序

#include <stdio.h>
#include <stdlib.h>

    struct n{
        double value;
        char op;
        struct n *next;
    };

    void delete(struct n **head);
    void add_item(struct n **ptr, double data);

    int main(){
        struct n *head = NULL;
        double result;
        add_item(&head, 5);
        add_item(&head, 3);
        head->op = '*';
        result = (head->next)->value * head->value;
        (head->next)->value = result;
        delete(&head);
        printf("%lf\n",head->value);
        free(head); 
        return 0;        
    }

void add_item(struct n **ptr, double data)
{
        struct n *item = malloc(sizeof *item);

        item->value = data;
        item->next = *ptr;
        item->op = '?';
        *ptr = item;
}

void delete(struct n **head)
{
    if ( *head )
    {
        struct n *temp = *head;
        *head = ( *head )->next;
        free( temp );
    }
}

它的输出是

15.000000

【讨论】:

  • 我收到error: invalid initializer struct n temp = *head;
  • @MichaelangeloMeucci 你有没有像我展示的那样通过头部 delete( &head );并声明 struct n **head? 之类的参数?
  • @MichaelangeloMeucci 哦,对不起。有一个错字。必须是 struct n *temp = *head;在函数内。
  • 我收到free(): double free detected in tcache 2
  • @MichaelangeloMeucci 显示您在 main 中调用 delete 并在 main 中调用 free 的所有语句。
【解决方案2】:

与 C 的一般情况一样,要让函数有权更改通过参数列表传递给它的对象,必须传递对象的 地址,而不是对象本身。 (read more on this here)

在本例中,对象head,如果要以任何方式更改,则需要将其地址&amp;head)传递给函数,而不是对象本身.
所以声明:

delete(head); //passing the object will not allow it to be changed

应该改为

delete(&head); //The object's address is passed, allowing the object to be changed

并且因为被传递的对象是作为指针创建的:struct n *head = NULL;,所以delete 函数的原型需要在其参数中容纳指针的地址。这是通过指向指针的指针完成的:

void delete(struct n **head);//accommodates the address of a pointer object

然后,在 delete 函数中,对对象本身(现在是 *head)进行更改。

   void delete(struct n **head)
    {
        if (*head == NULL) return;
        struct n *temp = *head;
        *head = temp->next;//point head to next node
        free(temp);//free old head
    }

相反,在您的void add_item(struct n **ptr, double *data); 函数中,data 不需要更改,只在正文中使用。实际上,在您的代码中调用它的方式是发送数据的正确方式:

add_item(&head, 5);  // 2nd argument passes object directly, i.e. not an address

因此,由于函数需要数据本身,而不是指向数据的指针,因此更改原型以适应:

void add_item(struct n **ptr, double data);

相应地更改代码正文中的代码。

【讨论】: