記錄了學習 “一生一芯” 時(更確切地説是學習 “Learn C The Hard Way” 時)遇到的
LIST_FOREACH鏈表遍歷宏。該宏的精髓在於使用 V 和 _node 雙指針機制,以確保即使在複雜場景下(如用户誤改指針),循環的健壯性和遍歷的正確性也不會被破壞。
LIST_FOREACH的定義
#define LIST_FOREACH(L, S, M, V) ListNode *_node = NULL;\
ListNode *V = NULL;\
for(V = _node = L->S; _node != NULL; V = _node = _node->M)
這個定義出現在笨辦法學C-中文版 練習32,用於雙向鏈表的遍歷。
用途與宏展開
用途
這個宏定義實現了對雙向鏈表的正向遍歷和反向遍歷,其中:
| 參數 | 含義 | 示例 |
|---|---|---|
L |
鏈表結構體指針 (List) | list (例如一個包含 head 和 tail 字段的結構體) |
S |
起始結點名 (Start) | head (如果從頭開始遍歷) 或 tail (如果從尾部開始遍歷) |
M |
移動到下一個節點的字段名 (Move) | next (用於單向或向前遍歷) 或 prev (用於向後遍歷) |
V |
當前節點變量名 (Variable) | cur 或 node (用户在循環體中使用的迭代變量) |
宏展開的邏輯分析
-
局部變量聲明
ListNode *_node = NULL; ListNode *V = NULL;-
_node: 這是一個臨時的內部工作變量,它總是指向下一個要檢查的節點。在循環的初始化和步進部分,它用於保存和移動。 -
V: 這是用户指定的當前節點變量。在每次循環迭代中,它指向用户當前處理的節點。
-
-
for循環for(V = _node = L->S; _node != NULL; V = _node = _node->M)很容易看懂。需要注意的是,運用了連等賦值的右結合性。例如,在步進部分
V = _node = _node->M中:- 最右邊先執行:
_node->M計算出下一個節點的地址。 - 向左賦值: 將該地址賦給
_node,完成_node的移動。 - 再向左賦值: 將
_node(已更新)的值賦給V,完成V的同步。
- 最右邊先執行:
為什麼同時存在V和_node
在這個宏中,V和_node兩個宏變量是同步賦值的。那麼,為什麼需要同時設置這兩個變量呢?
宏變量V和_node的作用
| 變量 | 完整名稱/別名 | 作用/職責核心職能 |
|---|---|---|
V |
用户變量/當前節點 | 供用户在循環體內部使用,代表當前正在處理的節點。接口:提供給用户操作。 |
_node |
內部變量/工作指針 | 供宏內部使用,負責驅動循環的條件檢查和步進計算。引擎:驅動循環邏輯。 |
健壯性考慮
這樣設計的原因可能是出於健壯性的考慮。如果只使用一個宏變量,例如只使用V,當遍歷過程中因為任何原因(例如調試、特殊邏輯、錯誤代碼)修改了 V 的值(使其指向另一個節點,或者賦值為 NULL),那麼步進表達式 V = V->M 將會徹底失敗,導致:
- 遍歷跳過節點。
- 遍歷提前終止。
- 訪問錯誤內存(如果
V被置為非法指針)。