从单链列表中删除条目

时间:2018-08-10 21:50:04

标签: c list pointers singly-linked-list

所以今天我在看The mind behind Linux | Linus Torvalds,Linus在视频中发布了两段代码,它们都用于删除单链接列表中的某个元素。

第一个(这是正常的):

void remove_list_entry(linked_list* entry) {
    linked_list* prev = NULL;
    linked_list* walk = head;
    while (walk != entry) {
        prev = walk;
        walk = walk->next;
    }
    if (!prev) {
        head = entry->next;
    } else {
        prev->next = entry->next;
    }
}

更好的一个:

void remove_list_entry(linked_list* entry) {
    // The "indirect" pointer points to the
    // *address* of the thing we'll update
    linked_list** indirect = &head;

    // Walk the list, looking for the thing that
    // points to the entry we want to remove
    while ((*indirect) != entry)
        indirect = &(*indirect)->next;

    // .. and just remove it
    *indirect = entry->next;
}

所以我听不懂第二段代码,当*indirect = entry->next;求值时会发生什么?我不明白为什么会导致删除某些条目。有人请解释一下,谢谢!

4 个答案:

答案 0 :(得分:4)

  

*indirect = entry->next;求值时会发生什么?我不明白为什么会导致删除某些条目。

我希望您对双指针有清楚的认识。 1)

假设以下内容:
节点结构为

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

并且链接列表具有5个节点,并且entry指针指向列表中的第二个节点。内存中的视图如下所示:

                          entry -+
   head                          |
      +---+     +-------+     +-------+     +-------+     +-------+     +--------+
      |   |---->| 1 |   |---->| 2 |   |---->| 3 |   |---->| 4 |   |---->| 5 |NULL|
      +---+     +-------+     +-------+     +-------+     +-------+     +--------+

此声明:

linked_list** indirect = &head;

将使indirect指针指向head

                         entry -+
  head                          |
     +---+     +-------+     +-------+     +-------+     +-------+     +--------+
     |   |---->| 1 |   |---->| 2 |   |---->| 3 |   |---->| 4 |   |---->| 5 |NULL|
     +---+     +-------+     +-------+     +-------+     +-------+     +--------+
       ^
       |
     +---+
     |   |
     +---+
   indirect

while循环

    while ((*indirect) != entry)

*indirect将给出第一个节点的地址,因为head指向第一个节点,并且由于entry指向第二个节点,因此循环条件的值为true代码将执行:

indirect = &(*indirect)->next;

这将使indirect指针指向第一个节点的next指针。内存视图:

                          entry -+
   head                          |
      +---+     +-------+     +-------+     +-------+     +-------+     +--------+
      |   |---->| 1 |   |---->| 2 |   |---->| 3 |   |---->| 4 |   |---->| 5 |NULL|
      +---+     +-------+     +-------+     +-------+     +-------+     +--------+
                      ^
                      |
                    +---+
                    |   |
                    +---+
                  indirect

现在将评估while循环条件。因为indirect指针现在指向第一个节点的next,所以*indirect将给出第二个节点的地址,并且由于entry指向第二个节点,因此循环条件求值到false并退出循环。
现在将执行以下代码:

*indirect = entry->next;

对第一个节点的*indirect的{​​{1}}解除引用,现在为它分配了next指针所指向的节点的next。内存视图:

entry

现在,第一个节点的 entry -+ head | +---+ +-------+ +-------+ +-------+ +-------+ +--------+ | |---->| 1 | |-- | 2 | |---->| 3 | |---->| 4 | |---->| 5 |NULL| +---+ +-------+ \ +-------+ +-------+ +-------+ +--------+ *indirect \ / +------------+ 指向列表中的第三个节点,这样第二个节点将从列表中删除。

希望这能清除您所有的疑问。


编辑

David建议在评论中添加一些详细信息-为什么在next中需要使用(..)括号?

&(*indirect)->next的类型为indirect,这意味着它可以保存类型为linked_list **的指针的地址。 linked_list *将给出类型*indirect的指针,而linked_list *将给出其->next的指针。
但是我们不能写next,因为运算符*indirect->next的优先级高于一元->运算符。因此,*将被解释为*indirect->next,这在语法上是错误的,因为*(indirect->next)是指向指针的指针。 因此,我们需要在indirect附近使用()

此外,*indirect将被解释为&(*indirect)->next指针的地址&((*indirect)->next)


1)如果您不知道双指针的工作原理,请检查以下内容:

让我们举个例子:

next

在上述代码的#include <stdio.h> int main() { int a=1, b=2; int *p = &a; int **pp = &p; printf ("1. p : %p\n", (void*)p); printf ("1. pp : %p\n", (void*)pp); printf ("1. *p : %d\n", *p); printf ("1. *pp : %d\n", **pp); *pp = &b; // this will change the address to which pointer p pointing to printf ("2. p : %p\n", (void*)p); printf ("2. pp : %p\n", (void*)pp); printf ("2. *p : %d\n", *p); printf ("2. *pp : %d\n", **pp); return 0; } 语句中,您可以看到,无需直接访问指针*pp = &b;,我们就可以使用双指针p来更改其指向的地址,它指向指针pp,因为取消引用双指针p会得到指针pp

其输出:

p

内存视图如下所示:

1. p : 0x7ffeedf75a38
1. pp : 0x7ffeedf75a28
1. *p : 1
1. *pp : 1
2. p : 0x7ffeedf75a34   <=========== changed 
2. pp : 0x7ffeedf75a28
2. *p : 2
2. *pp : 2

答案 1 :(得分:1)

该条目并没有真正被“删除”,只是不再存在于列表中。 如果这是您的连锁店:

A --> B --> C --> D --> E --> ■

您想删除C,您实际上只是在链接它。它仍然存在于内存中,但是不再可以从您的数据结构中访问。

            C 
A --> B --------> D --> E --> ■

最后一行将B的next指针设置为D而不是C。

答案 2 :(得分:1)

第二个示例不是像第一个示例那样遍历列表中的条目,而是通过指针遍历列表中的条目。这使第二个示例可以简单地得出您所询问的语句的结论,即用英语“设置用于指向我要从列表中删除的条目的指针,以便它现在指向该条目的任何内容”指向“。换句话说,它使指向您要删除的条目的指针指向 past 您要删除的条目。

第一个示例必须有一种特殊的方式来处理要删除的条目(列表中的第一个条目)的独特情况。因为第二个示例循环遍历指针(以&head开头),所以它没有特殊情况。

答案 3 :(得分:0)

* indirect = entry-> next; 只需将其移至下一个节点 您需要删除条目一 因此,您必须在入口节点之前指向..入口节点的下一个 因此,您的循环应在输入之前停止 while(((*间接)->下一个!=条目)         间接=&(*间接)->下一个

(*间接)->下一个= entry->下一个

希望对您有帮助