[英]delete an entry from a singly-linked list
所以今天我在看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;
時會發生什么評價? 我不明白為什么它會導致刪除某個條目。 請哪位大神解釋一下,謝謝!
當
*indirect = entry->next;
時會發生什么*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
解引用到next
首節點,它現在被指定為next
該節點的entry
指針指向。 內存中的視圖:
entry -+
head |
+---+ +-------+ +-------+ +-------+ +-------+ +--------+
| |---->| 1 | |-- | 2 | |---->| 3 | |---->| 4 | |---->| 5 |NULL|
+---+ +-------+ \ +-------+ +-------+ +-------+ +--------+
*indirect \ /
+------------+
現在,第一個節點的next
一個節點指向列表中的第三個節點,這樣就可以從列表中刪除第二個節點。
希望這清楚你所有的疑慮。
編輯 :
大衛在評論中建議添加一些細節 - 為什么(..)
&(*indirect)->next
所需的(..)
括號&(*indirect)->next
?
indirect
的類型是linked_list **
,這意味着它可以保存linked_list *
類型的指針的地址。 *indirect
將給出linked_list *
類型的指針,而->next
將給出它的next
指針。
但是我們不能寫*indirect->next
因為operator ->
的優先級高於unary *
operator。 因此, *indirect->next
將被解釋為*(indirect->next)
,這在語法上是錯誤的,因為indirect
是指向指針的指針。 因此我們需要()
圍繞*indirect
。
另外, &(*indirect)->next
將被解釋為&((*indirect)->next)
,這是next
指針的地址。
1)如果您不知道雙指針的工作原理,請查看以下內容:
讓我們舉一個例子:
#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
內存中的視圖將是這樣的:
//Below in the picture
//100 represents 0x7ffeedf75a38 address
//200 represents 0x7ffeedf75a34 address
//300 represents 0x7ffeedf75a28 address
int *p = &a
p a
+---+ +---+
|100|---->| 1 |
+---+ +---+
int **pp = &p;
pp p a
+---+ +---+ +---+
|300|---->|100|---->| 1 |
+---+ +---+ +---+
*pp = &b;
pp p b
+---+ +---+ +---+
|300|---->|200|---->| 2 |
+---+ +---+ +---+
^^^^^ ^^^^^
該條目並未真正“刪除”,它已不在列表中。 如果這是你的鏈:
A --> B --> C --> D --> E --> ■
而你想刪除C,你真的只是鏈接它。 它仍然存在於內存中,但不再可以從您的數據結構中訪問。
C
A --> B --------> D --> E --> ■
最后一行將B的next
指針設置為D而不是C.
如第一個示例所示,第二個示例不是循環遍歷列表中的條目,而是遍歷指向列表中條目的指針 。 這允許第二個例子用您所詢問的語句得出簡單的結論,在英語中是“設置用於指向我想要從列表中刪除的條目的指針,以便它現在指向該條目指着“。 換句話說,它使這是指向您刪除點過去 ,你要移除項條目的指針。
第一個示例必須有一種特殊的方法來處理要刪除的條目的唯一大小寫,作為列表中的第一個條目。 因為第二個例子循環遍歷指針(以&head開頭),所以它沒有特殊情況。
* indirect = entry-> next; 那只是將它移動到下一個節點你需要刪除一個條目所以你必須指向..在入口節點之前的下一個入口節點所以你的循環應該在入口之前停止while((* indirect) - > next!= ()間接=&(*間接) - >下一步
(*間接) - >下一個=輸入 - >下一個
我希望能幫助你
如果你重寫indirect = &(*indirect)->next; 這將更容易理解。 As 間接 = &((*indirect)->next);
while 循環將給我們一個 next 指針的地址,該地址屬於某個節點,其中 next 指針指向條目。所以最后一條語句實際上是在更改這個 next 指針的值,使其不指向條目了。 並且在入口為頭的特殊情況下,將跳過while循環,最后一行改變頭指針的值,使其指向入口的下一個節點
這個例子既是一種特別是操作鏈表結構的好方法,也是一個非常好的方法來展示指針的力量。
當你從單鏈表中刪除一個元素時,你必須讓前一個節點指向下一個節點,繞過你要刪除的節點。 例如,如果您要刪除節點E
,那么無論它用於指向E
的列表指針是什么,您都必須使其指向E.next
指向的任何內容。
現在,問題在於“用於指向E
的任何列表指針”有兩種可能性。 很多時候,是某個前一個節點的next
指針指向E
。 但是,如果E
恰好是列表中的第一個節點,這意味着列表中沒有前一個節點,並且它是指向E
的頂級列表指針——在 Linus 的示例中,這就是變量head
。
所以在 Linus 的第一個“正常”示例中,有一個if
語句。 如果有前一個節點,代碼設置prev->next
指向下一個節點。 但是如果沒有前一個節點,這意味着它正在刪除列表頭部的節點,因此它將head
設置為指向下一個節點。
盡管這不是世界末日,但它是兩個獨立的分配和一個if
條件來處理我們在英語中所認為的“無論是用於指向E
的列表指針”。 優秀程序員的關鍵標志之一是能夠准確無誤地嗅出這種不必要的冗余並用更干凈的東西取而代之。
在這種情況下,關鍵的見解是我們可能想要更新的兩件事,即head
或prev->next
,都是指向列表節點或linked_list *
的指針。 指針最擅長的事情之一就是指向我們關心的事情,即使根據情況,這件事可能是幾件不同的事情中的一件。
由於我們關心的是指向linked_list
的指針,指向我們關心的東西的指針將是指向linked_list
或linked_list **
的指針。
這正是 Linus 的“更好”示例中的indirect
變量。 從字面上看,它是一個指向“用於指向E
的任何列表指針”的指針(或者,在實際代碼中,不是E
,而是被刪除的傳入entry
)。 起初, indirect
指針指向head
,但后來,在我們開始遍歷列表以找到要刪除的節點后,它指向指向我們的節點(前一個節點)的next
指針'重新看。 所以,在任何情況下, *indirect
(也就是由indirect
指向的指針)就是我們要更新的指針。 這正是神奇的線
*indirect = entry->next;
在“更好”的例子中。
需要注意的另一件事(盡管這可能使代碼一開始更加神秘)是indirect
變量也代替了第一個示例中使用的walk
變量。 也就是說,第一個示例在任何地方都使用walk
,“更好”的示例使用*indirect
。 但這是有道理的:我們需要遍歷列表中的所有節點,尋找entry
。 所以我們需要一個指針來越過這些節點——這就是第一個例子中walk
變量所做的。 但是,當我們找到要刪除的條目時,指向該條目的指針將是“用於指向E
的任何列表指針”——它將是要更新的指針。 在第一個示例中,我們不能將walk
設置為prev->next
這只會更新本地walk
變量,而不是head
或列表中的next
指針之一。 但是通過使用indirect
指向(間接)遍歷列表的指針, *indirect
(即indirect
指向的指針)總是指向我們正在查看的節點的原始指針(而不是坐在其中的副本) walk
),這意味着我們可以通過說*indirect = entry->next
來有效地更新它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.