如何用C语言宏实现链表遍历?
摘要:记录了学习 “一生一芯” 时(更确切地说是学习 “Learn C The Hard Way” 时)遇到的 LIST_FOREACH 链表遍历宏。该宏的精髓在于使用 V 和 _node 双指针机制,以确保即使在复杂场景下(如用户误改指针),循
记录了学习 “一生一芯” 时(更确切地说是学习 “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 被置为非法指针)。
