【问题标题】:Problem deleting the only node from a linked list从链表中删除唯一节点的问题
【发布时间】:2021-04-13 16:13:53
【问题描述】:

如果某个节点的值与用户输入的值匹配,我正在尝试创建一个函数来删除某个节点。如果只有一个节点,我创建了一个案例,但是在使用free(curr_node)删除节点并调用traverse函数后,cmd会无休止地打印出数字。我错过了什么?

typedef struct Node {
    int data;
    struct Node *next;
}Node;

Node *head = NULL;
int node_number = 0;

void traverse(Node *head, int count) {
    int i = 1;

    if(head == NULL) {
        printf("No nodes to traverse!");
        return;
    }

    printf("%d node(s), with their respective value: \n", count);
    while(head != NULL) {
        if(i == count)
            printf("%d\n", head->data);
        else
            printf("%d-", head->data);
        head = head->next;
        i++;
    }
}

void delete_item(Node *head) {
    Node *curr_node = head;

    int value;

    printf("Enter value to search by: ");
    scanf("%d", &value);

    while(curr_node != NULL) {
        if(curr_node->data == value) {
            if(curr_node->next == NULL) {
                free(curr_node);
                head = NULL;
                printf("Node deleted successfully!\n");
                return;
            }
        }
        //curr_node = curr_node->next;
    }
}

Node *create_item() {
    Node *result = NULL;
    result = (Node *)malloc(sizeof(Node));

    if(result == NULL) {
        printf("Couldn't allocate memory!");
        return 0;
    }

    printf("Value of node %d: ", node_number + 1);
    scanf("%d", &result->data);
    result->next = NULL;
    node_number++;
    return result;
}

int main() {
    int nodes;

    Node *temp;
    head = create_item();
    
    delete_item(head);
    traverse(head, node_number);

    return 0;

【问题讨论】:

  • head within delete_item 是一个局部变量(包含您从调用者传递的列表头的地址)。因此,head = NULL; 对这个函数的调用者来说意味着 nothing,除了说它将使headmain 中的地址仍然持有无效,并且因此,此后的任何取消引用都会调用未定义的行为。通过地址传递指针并使用间接修改它,或者返回新的列表地址作为返回结果,利用其他未使用的函数结果。实际功能被破坏也无济于事。
  • @WhozCraig 自从我将“head”设为全局函数之后,它是如何成为局部变量的?
  • 作为一般提示,您可以将适当的列表类型定义为struct,其中包含指向每次添加或删除节点时更新的 head 和 size 字段。
  • void delete_item(Node *head) current 列表头,在调用者代码中捕获该结果)。
  • @WhozCraig 我可以使用双指针来传递对 head 的引用,但这意味着我必须修复代码,因为我传递的是双指针,而不是单个指针,或者我应该同时传递 @ 987654331@ 和 **head 并仅在这种情况下使用 **head

标签: c pointers linked-list


【解决方案1】:

调用者未捕获对head 的更改。事实上,head 实际上是delete_node 的一个本地 变量,以及对它的任何更改(不要与使用延迟操作更改通过 混淆),未被调用者捕获。

C 中的所有函数参数都是按值。有人会说“数组不是这样”;他们是。在表达式中使用时,数组的“值”由语言标准定义为引用第一个元素地址的临时指针。 IE。仍然按价值计算,它只是价值不是您所期望的。但在您的情况下,head 是按价值计算的。如果你有一个函数void foo(int x),你已经知道在foo 中修改x 不会改变他们传递的调用者的int;这里也是如此。仅仅因为它的指针没有区别。如果你想修改调用者参数,你必须修路才能到达那里。

这附近有两所普通学校。

  1. 使用指向指针参数的指针并在main 中传递head 的地址。这需要遵循指向指针的指针以获取实际的列表头,但也允许您修改调用者指针。

  2. 使用函数的返回结果将当前列表头传回给调用者(即执行任何操作之后的头。

第一个更复杂,但允许您将返回结果用于其他目的(如错误检查、提示)。后者更容易实现。两者都会完成你想要的。前者如下图:

void delete_item(Node **head) 
{
    int value;

    printf("Enter value to search by: ");
    if (scanf("%d", &value) == 1)
    {
        while (*head)
        {
            if ((*head)->data == value)
            {
                void *tmp = *head;
                *head = (*head)->next;
                free(tmp);
                
                printf("Node deleted successfully!\n");
                break;
            }

            head = &(*head)->next;
        }
    }
}

调用者main在这种情况下也需要修改:

delete_item(&head); // <== note passed by address now.

【讨论】:

  • 感谢您的明确解释。我通过传递*head**head 并分别使用它们来做到这一点,但我认为这是一种不好的做法,我猜?编辑:顺便说一句,为什么 &amp; 既然 (*head) 已经指向头部?
  • 首先,我不知道你此时做了什么。如果全局head 被声明为Node *head 并且您调用delete_item(*head);,则main 不可能使用上面的代码正确编译。这将通过Node,其中预计Node **。这也应该回答你的问题。从main 传递&amp;head 是必需的,因为delete_item 现在需要一个指向节点的指针,所以这就是我们给它的:(not in; of) 的地址指向节点的指针。请记住,指针变量仍然是变量。他们拥有地址,但他们自己也拥有地址。
  • 我的意思是我有void delete_item(Node *head, Node **head_ref) 并从main 传递delete_item(head, &amp;head)。这就是我所说的我通过它们并分别使用它们的意思(**head 用于唯一的节点情况)。我只是不明白为什么我们在head = &amp;(*head)-&gt;next; 中有&amp;,因为*head 已经指向节点?
  • 如果你使用我提供的,你可以用 one 参数来做到这一点。要回答您的问题,节点的next 成员是什么type?这是Node* 对吗?因此,应用&amp; 将产生Node **,这正是head 在函数我在上面写的 的上下文中的内容。在 调试器 中运行此代码并观察 head 指向的内容以及 *head 指向的内容,将更好地亲身体验其工作原理。我可以打字一个小时(实际上现在已经打字了),但在你看到它之前,它不会凝固。
  • 谢谢。也许我应该重新阅读它以更好地理解它。只是想问一下,我通过这两个方法都是不好的做法吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-10-05
  • 1970-01-01
  • 2011-05-12
  • 2020-09-10
  • 2011-06-06
相关资源
最近更新 更多