所以今天我在看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;
求值时会发生什么?我不明白为什么会导致删除某些条目。有人请解释一下,谢谢!
答案 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->下一个
希望对您有帮助