Stories

Detail Return Return

一生一芯中有趣的C語言宏:LIST_FOREACH 鏈表遍歷宏 - Stories Detail

記錄了學習 “一生一芯” 時(更確切地説是學習 “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 (例如一個包含 headtail 字段的結構體)
S 起始結點名 (Start) head (如果從頭開始遍歷) 或 tail (如果從尾部開始遍歷)
M 移動到下一個節點的字段名 (Move) next (用於單向或向前遍歷) 或 prev (用於向後遍歷)
V 當前節點變量名 (Variable) curnode (用户在循環體中使用的迭代變量)

宏展開的邏輯分析

  1. 局部變量聲明

    ListNode *_node = NULL;
    ListNode *V = NULL;
    
    • _node: 這是一個臨時的內部工作變量,它總是指向下一個要檢查的節點。在循環的初始化和步進部分,它用於保存移動

    • V: 這是用户指定的當前節點變量。在每次循環迭代中,它指向用户當前處理的節點。

  2. for循環

    for(V = _node = L->S; _node != NULL; V = _node = _node->M)
    

    很容易看懂。需要注意的是,運用了連等賦值的右結合性。例如,在步進部分 V = _node = _node->M 中:

    1. 最右邊先執行: _node->M 計算出下一個節點的地址。
    2. 向左賦值: 將該地址賦給 _node,完成 _node 的移動
    3. 再向左賦值: 將 _node(已更新)的值賦給 V,完成 V 的同步

為什麼同時存在V_node

在這個宏中,V_node兩個宏變量是同步賦值的。那麼,為什麼需要同時設置這兩個變量呢?

宏變量V_node的作用

變量 完整名稱/別名 作用/職責核心職能
V 用户變量/當前節點 用户在循環體內部使用,代表當前正在處理的節點接口:提供給用户操作。
_node 內部變量/工作指針 宏內部使用,負責驅動循環的條件檢查步進計算引擎:驅動循環邏輯。

健壯性考慮

這樣設計的原因可能是出於健壯性的考慮。如果只使用一個宏變量,例如只使用V,當遍歷過程中因為任何原因(例如調試、特殊邏輯、錯誤代碼)修改了 V 的值(使其指向另一個節點,或者賦值為 NULL),那麼步進表達式 V = V->M 將會徹底失敗,導致:

  • 遍歷跳過節點。
  • 遍歷提前終止
  • 訪問錯誤內存(如果 V 被置為非法指針)。

Add a new Comments

Some HTML is okay.