我們原來的寫法是把指標指向Node結構,Linus說的第二種寫法是把指標指向了Node結構內的next指標,如此而已。
摘要:Linus Torvalds坦言那些狡詐的通過檔案名稱尋找快取,然後又抱怨自己能力一般的核心“惡魔”才是他欣賞的;相反,很多人連低水平的核心編程都還沒學好。
幾周前, Linus Torvalds在Slashdot上回答了一些問題。其中有一條引發了開發人員們的強烈關注,當被問到他心目中的核心駭客時,他說自己這些日子已經不怎麼看代碼了,除非是幫別人審查。他稍微暫停了一下,坦言那些“狡猾”的通過檔案名稱尋找快取又抱怨自己能力一般的核心“惡魔”(駭客)才是他欣賞的。
他說:
相反,很多人連低水平的核心編程都還沒學好。像lockless用名字尋找(name lookup)功能即使不大也不複雜,卻是指標到指標的一個簡單及良好的使用方法。比如,我曾看見過許多人通過跟蹤上一頁條目刪除一個單向連結的清單項目,然後刪除該條目。例如:
- if (prev)
- prev->next = entry->next;
- else
- list_head = entry->next;
每當我看到這些的代碼,我會說:“此人不瞭解指標”。這還是一個可悲的、常見的問題。
如果開發人員能夠理解指標,只需要使用“指向該條目的指標”並初始化list_head,然後貫穿列表,此時無需使用任何條件陳述式即可刪除該條目,只需通過 *pp = entry->next。
我想我理解指標,但不幸的是,如果要實現刪除函數,我會一直保持跟蹤前面的列表節點。這裡是代碼草稿:
不理解指標的人做法:
- typedef struct node
- {
- struct node * next;
- ....
- } node;
-
- typedef bool (* remove_fn)(node const * v);
-
- // Remove all nodes from the supplied list for which the
- // supplied remove function returns true.
- // Returns the new head of the list.
- node * remove_if(node * head, remove_fn rm)
- {
- for (node * prev = NULL, * curr = head; curr != NULL; )
- {
- node * next = curr->next;
- if (rm(curr))
- {
- if (prev)
- prev->next = curr->next;
- else
- head = curr->next;
- free(curr);
- }
- else
- prev = curr;
- curr = next;
- }
- return head;
- }
這個鏈表很簡單,但可以把每個節點的指標和sentinel值構建成了一個完美的結構體,但是修改這個表的代碼需要很精妙。難怪鏈表功能會常出現在許多面試環節中。
上面執行的代碼是處理從列表頭中刪除任何節點所需的條件。
現在,讓我們好好記住Linus Torvalds執行代碼。在這種情況下,我們通過一個指標指向列表頭來貫穿列表遍曆修改。
Two star programming:
- void remove_if(node ** head, remove_fn rm)
- {
- for (node** curr = head; *curr; )
- {
- node * entry = *curr;
- if (rm(entry))
- {
- *curr = entry->next;
- free(entry);
- }
- else
- curr = &entry->next;
- }
- }
好多了!最關鍵的部分在於:鏈表中的連結都是指標,因此指標到指標是修改鏈表的首選方案。
改進版的remove_if()是一個使用雙重星號的例子,雙重星號象徵著兩重間接定址,再加一個星(third star)又會太過多餘。