Linux 内核链表宏的详细解释

🔧 Linux 内核链表结构概览

Linux 内核中的链表结构定义在头文件 <linux/list.h> 中。核心结构是:

struct list_head {struct list_head *next, *prev;
};

它表示一个双向循环链表的节点。链表的所有操作都围绕这个结构体展开。


🧩 链表宏总览

以下是常用链表宏的功能简介:

宏名作用简述
LIST_HEAD(name)定义并初始化一个链表头
INIT_LIST_HEAD(ptr)初始化一个链表头指针
list_add(new, head)new 节点插入到 head 之后
list_add_tail(new, head)new 节点插入到 head 之前(尾部)
list_del(entry)删除节点
list_replace(old, new)替换一个节点
list_empty(head)检查链表是否为空
list_entry(ptr, type, member)获取结构体指针
list_first_entry(ptr, type, member)获取第一个元素结构体指针
list_next_entry(pos, member)获取下一个元素结构体指针
list_for_each(pos, head)遍历链表(指针)
list_for_each_entry(pos, head, member)遍历链表(结构体)
list_for_each_safe(pos, n, head)安全遍历链表(可删除)
list_for_each_entry_safe(pos, n, head, member)安全遍历结构体(可删除)
list_for_each_prev(pos, head)反向遍历
list_for_each_entry_reverse(pos, head, member)反向结构体遍历
list_prepare_entry(pos, head)安全地返回前一个 entry

✅ 1. LIST_HEAD(name)

📌 作用

定义并初始化一个链表头。

🧾 参数
  • name:链表头的名字(变量名)

🧪 示例
// list_head_example1.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list); // 定义并初始化一个链表头int main(void)
{if (list_empty(&my_list))printf("链表为空\n");elseprintf("链表非空\n");return 0;
}

✅ 编译方式(需要支持内核头文件环境)

gcc -o list_head_example1 list_head_example1.c

📤 输出结果

链表为空

✅ 2. INIT_LIST_HEAD(ptr)

📌 作用

初始化一个已经定义好的链表头指针,确保其 nextprev 都指向自身,形成一个空的循环链表。

🧾 参数
  • ptr:指向 struct list_head 类型的指针变量,表示链表头。

💡 适用场景

当链表头不是通过 LIST_HEAD(name) 宏静态定义的,而是动态分配的或作为结构体成员时,需要使用此宏进行初始化。


🧪 示例
// init_list_head_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};int main(void)
{struct list_head my_listINIT_LIST_HEAD(&my_list)  // 初始化链表头if (list_empty(&my_list))printf("链表为空\n")elseprintf("链表非空\n")return 0
}

📤 输出结果

链表为空

✅ 3. list_add(new, head)

📌 作用

将新节点 new 插入到 head 节点之后,即作为链表的第一个元素插入(头插法)。

🧾 参数
  • new:指向要插入的新节点的 struct list_head 指针。

  • head:指向链表头节点的 struct list_head 指针。

💡 特点

新节点插入在 head 后面,head->next 指向新节点,适合实现栈结构(LIFO)。


🧪 示例
// list_add_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list)  // 初始化链表头int main(void)
{struct my_node *node1 = malloc(sizeof(*node1))struct my_node *node2 = malloc(sizeof(*node2))node1->data = 111node2->data = 222INIT_LIST_HEAD(&node1->list)INIT_LIST_HEAD(&node2->list)list_add(&node1->list, &my_list)list_add(&node2->list, &my_list)struct my_node *poslist_for_each_entry(pos, &my_list, list){printf("data: %d\n", pos->data)}free(node1)free(node2)return 0
}

📤 输出结果

data: 222
data: 111

✅ 4. list_add_tail(new, head)

📌 作用

将新节点 new 插入到 head 节点之前,即作为链表的最后一个元素插入(尾插法)。

🧾 参数
  • new:指向要插入的新节点的 struct list_head 指针。

  • head:指向链表头节点的 struct list_head 指针。

💡 特点

新节点成为链表的“尾部”节点,适合实现队列结构(FIFO)。


🧪 示例
// list_add_tail_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list)  // 初始化链表头int main(void)
{struct my_node *node1 = malloc(sizeof(*node1))struct my_node *node2 = malloc(sizeof(*node2))node1->data = 111node2->data = 222INIT_LIST_HEAD(&node1->list)INIT_LIST_HEAD(&node2->list)list_add_tail(&node1->list, &my_list)list_add_tail(&node2->list, &my_list)struct my_node *poslist_for_each_entry(pos, &my_list, list){printf("data: %d\n", pos->data)}free(node1)free(node2)return 0
}

📤 输出结果

data: 111
data: 222

✅ 5. list_del(entry)

📌 作用

将链表中的某个节点 entry 从链表中删除,并不会释放内存,只断开指针。

🧾 参数
  • entry:指向要删除的 struct list_head 节点。

⚠️ 注意
  • 被删除的节点仍然存在于内存中,需要手动 free()

  • 删除后,该节点的 nextprev 不会自动清空(可选使用 list_del_init() 初始化)。

    list_del(&node1->list) 删除了 node1 节点,但是 node1->list.prevnode1->list.next 依然保留了原来的值。它们指向链表中曾经链接到 node1 的节点。因此,删除后的节点仍然有 nextprev 指针,它们并没有被清空。

    list_del_init() 也用于从链表中删除节点,它不仅删除节点,还将被删除节点的 nextprev 指针设置为链表头节点的指针(即初始化节点)。这通常用于防止误用已经删除的节点。


🧪 示例
// list_del_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list)int main(void)
{struct my_node *node1 = malloc(sizeof(*node1))struct my_node *node2 = malloc(sizeof(*node2))struct my_node *node3 = malloc(sizeof(*node3))node1->data = 111node2->data = 222node3->data = 333INIT_LIST_HEAD(&node1->list)INIT_LIST_HEAD(&node2->list)INIT_LIST_HEAD(&node3->list)list_add_tail(&node1->list, &my_list)list_add_tail(&node2->list, &my_list)list_add_tail(&node3->list, &my_list)// 删除 node2 节点list_del(&node2->list)struct my_node *poslist_for_each_entry(pos, &my_list, list){printf("data: %d\n", pos->data)}free(node1)free(node2)  // 注意:即使被删除,也要手动释放free(node3)return 0
}

📤 输出结果

data: 111
data: 333

✅ 6. list_replace(old, new)

📌 作用

将链表中的一个节点 old 替换为另一个节点 new,原位置不变,链表结构保持完整。

🧾 参数
  • old:要被替换掉的节点(struct list_head *)。

  • new:用于替换的新节点(struct list_head *)。

💡 特点
  • 替换后,old 脱离链表,但链表中整体顺序保持;

  • 不初始化 old,如需复用需手动 INIT_LIST_HEAD()


🧪 示例
// list_replace_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list)int main(void)
{struct my_node *node1 = malloc(sizeof(*node1))struct my_node *node2 = malloc(sizeof(*node2))struct my_node *node3 = malloc(sizeof(*node3))  // 替换者node1->data = 111node2->data = 222node3->data = 999  // 用这个替换 node2INIT_LIST_HEAD(&node1->list)INIT_LIST_HEAD(&node2->list)INIT_LIST_HEAD(&node3->list)list_add_tail(&node1->list, &my_list)list_add_tail(&node2->list, &my_list)// 替换 node2 为 node3list_replace(&node2->list, &node3->list)struct my_node *poslist_for_each_entry(pos, &my_list, list){printf("data: %d\n", pos->data)}free(node1)free(node2)free(node3)return 0
}

📤 输出结果

data: 111
data: 999

✅ 7. list_empty(head)

📌 作用

判断链表是否为空(即 head->next == headhead->prev == head)。

🧾 参数
  • head:指向链表头的 struct list_head *

💡 返回值
  • 空链表 → 返回 true(非 0);

  • 非空链表 → 返回 false(0);


🧪 示例
// list_empty_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list)int main(void)
{if (list_empty(&my_list))printf("链表最初是空的\n")struct my_node *node = malloc(sizeof(*node))node->data = 100INIT_LIST_HEAD(&node->list)list_add(&node->list, &my_list)if (!list_empty(&my_list))printf("插入后链表非空\n")list_del(&node->list)if (list_empty(&my_list))printf("删除后链表再次为空\n")free(node)return 0
}

📤 输出结果

链表最初是空的
插入后链表非空
删除后链表再次为空

✅ 8. list_entry(ptr, type, member)

📌 作用

通过链表节点指针 ptr,获取它所在的结构体地址。

🧾 参数
  • ptr:指向某个 struct list_head 节点的指针;

  • type:包含该 list_head 的结构体类型;

  • member:结构体中 list_head 成员的名字。

💡 功能原理

通过偏移量(container_of)从 list_head 成员地址回推结构体起始地址。


🧪 示例
// list_entry_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list)int main(void)
{struct my_node *node = malloc(sizeof(*node))node->data = 123INIT_LIST_HEAD(&node->list)list_add(&node->list, &my_list)// 直接使用 list_entry 获取结构体指针struct list_head *first = my_list.nextstruct my_node *entry = list_entry(first, struct my_node, list)printf("获取的结构体 data = %d\n", entry->data)list_del(&node->list)free(node)return 0
}

📤 输出结果

获取的结构体 data = 123

✅ 9. list_first_entry(ptr, type, member)

📌 作用

获取链表 ptr 中的第一个元素(结构体指针),等价于:

list_entry((ptr)->next, type, member)
🧾 参数
  • ptr:链表头指针(struct list_head *);

  • type:结构体类型;

  • member:结构体中 list_head 的成员名。


🧪 示例
// list_first_entry_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list)int main(void)
{struct my_node *node1 = malloc(sizeof(*node1))struct my_node *node2 = malloc(sizeof(*node2))node1->data = 10node2->data = 20INIT_LIST_HEAD(&node1->list)INIT_LIST_HEAD(&node2->list)list_add_tail(&node1->list, &my_list)list_add_tail(&node2->list, &my_list)struct my_node *first = list_first_entry(&my_list, struct my_node, list)printf("第一个节点的数据是:%d\n", first->data)free(node1)free(node2)return 0
}

 📤 输出结果

第一个节点的数据是:10

✅ 10. list_last_entry(ptr, type, member)

📌 作用

获取链表 ptr 中的最后一个元素(结构体指针),等价于:

list_entry((ptr)->prev, type, member)

🧪 示例
// list_last_entry_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list)int main(void)
{struct my_node *node1 = malloc(sizeof(*node1))struct my_node *node2 = malloc(sizeof(*node2))node1->data = 10node2->data = 20INIT_LIST_HEAD(&node1->list)INIT_LIST_HEAD(&node2->list)list_add_tail(&node1->list, &my_list)list_add_tail(&node2->list, &my_list)struct my_node *last = list_last_entry(&my_list, struct my_node, list)printf("最后一个节点的数据是:%d\n", last->data)free(node1)free(node2)return 0
}

📤 输出结果

最后一个节点的数据是:20

✅ 11. list_for_each(pos, head)

📌 作用

遍历链表的每个 struct list_head 节点指针(不自动转换为结构体)。

🧾 参数
  • posstruct list_head *,遍历用的临时变量;

  • head:链表头指针。


🧪 示例
// list_for_each_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list)int main(void)
{struct my_node *node1 = malloc(sizeof(*node1))struct my_node *node2 = malloc(sizeof(*node2))node1->data = 100node2->data = 200INIT_LIST_HEAD(&node1->list)INIT_LIST_HEAD(&node2->list)list_add_tail(&node1->list, &my_list)list_add_tail(&node2->list, &my_list)struct list_head *poslist_for_each(pos, &my_list){struct my_node *entry = list_entry(pos, struct my_node, list)printf("遍历到节点数据: %d\n", entry->data)}free(node1)free(node2)return 0
}

📤 输出结果

遍历到节点数据: 100  
遍历到节点数据: 200

✅ 12. list_for_each_prev(pos, head)

📌 作用

从链表尾部开始向前遍历链表。与 list_for_each 相对,后者从链表头开始向后遍历。

🧾 参数
  • posstruct list_head *,用于遍历的临时变量;

  • head:链表头指针。


🧪 示例
// list_for_each_prev_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list)int main(void)
{struct my_node *node1 = malloc(sizeof(*node1))struct my_node *node2 = malloc(sizeof(*node2))node1->data = 100node2->data = 200INIT_LIST_HEAD(&node1->list)INIT_LIST_HEAD(&node2->list)list_add_tail(&node1->list, &my_list)list_add_tail(&node2->list, &my_list)struct list_head *poslist_for_each_prev(pos, &my_list){struct my_node *entry = list_entry(pos, struct my_node, list)printf("倒序遍历到节点数据: %d\n", entry->data)}free(node1)free(node2)return 0
}

📤 输出结果

倒序遍历到节点数据: 200  
倒序遍历到节点数据: 100

✅ 13. list_for_each_entry(pos, head, member)

📌 作用

通过 struct list_head 节点遍历整个链表,pos 是每次遍历时对应的结构体类型指针。

🧾 参数
  • pos:结构体类型指针,遍历时指向每个链表元素;

  • head:链表头指针;

  • member:链表结构体中的 list_head 成员名。


🧪 示例
// list_for_each_entry_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list)int main(void)
{struct my_node *node1 = malloc(sizeof(*node1))struct my_node *node2 = malloc(sizeof(*node2))node1->data = 10node2->data = 20INIT_LIST_HEAD(&node1->list)INIT_LIST_HEAD(&node2->list)list_add_tail(&node1->list, &my_list)list_add_tail(&node2->list, &my_list)struct my_node *poslist_for_each_entry(pos, &my_list, list){printf("遍历到节点数据: %d\n", pos->data)}free(node1)free(node2)return 0
}

📤 输出结果

遍历到节点数据: 10  
遍历到节点数据: 20

✅ 14. list_for_each_entry_reverse(pos, head, member)

📌 作用

从链表尾部向前遍历,类似于 list_for_each_entry,但是顺序是反的。

🧾 参数
  • pos:结构体类型指针;

  • head:链表头指针;

  • member:链表结构体中的 list_head 成员名。


🧪 示例
// list_for_each_entry_reverse_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list)int main(void)
{struct my_node *node1 = malloc(sizeof(*node1))struct my_node *node2 = malloc(sizeof(*node2))node1->data = 10node2->data = 20INIT_LIST_HEAD(&node1->list)INIT_LIST_HEAD(&node2->list)list_add_tail(&node1->list, &my_list)list_add_tail(&node2->list, &my_list)struct my_node *poslist_for_each_entry_reverse(pos, &my_list, list){printf("倒序遍历到节点数据: %d\n", pos->data)}free(node1)free(node2)return 0
}

📤 输出结果

倒序遍历到节点数据: 20  
倒序遍历到节点数据: 10

✅ 15. list_for_each_safe(pos, n, head)

📌 作用

遍历链表的同时,安全地删除当前节点,防止因删除节点而导致链表破损。

🧾 参数
  • pos:结构体类型指针,用于遍历每个节点;

  • n:临时指针,用于保存当前节点的下一个节点;

  • head:链表头指针。


🧪 示例
// list_for_each_safe_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list)int main(void)
{struct my_node *node1 = malloc(sizeof(*node1))struct my_node *node2 = malloc(sizeof(*node2))node1->data = 10node2->data = 20INIT_LIST_HEAD(&node1->list)INIT_LIST_HEAD(&node2->list)list_add_tail(&node1->list, &my_list)list_add_tail(&node2->list, &my_list)struct my_node *pos, *nlist_for_each_safe(pos, n, &my_list){printf("遍历到节点数据: %d\n", pos->data)list_del(&pos->list)  // 删除当前节点free(pos)}return 0
}

📤 输出结果

遍历到节点数据: 10  
遍历到节点数据: 20

注意:此示例中,节点在遍历时被安全地删除。

✅ 16. list_for_each_entry_safe(pos, n, head, member)

📌 作用

安全地遍历链表并删除节点,避免删除过程中破坏链表结构。

🧾 参数
  • pos:结构体类型指针;

  • n:临时指针;

  • head:链表头指针;

  • member:结构体中的 list_head 成员名。


🧪 示例
// list_for_each_entry_safe_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list)int main(void)
{struct my_node *node1 = malloc(sizeof(*node1))struct my_node *node2 = malloc(sizeof(*node2))node1->data = 10node2->data = 20INIT_LIST_HEAD(&node1->list)INIT_LIST_HEAD(&node2->list)list_add_tail(&node1->list, &my_list)list_add_tail(&node2->list, &my_list)struct my_node *pos, *nlist_for_each_entry_safe(pos, n, &my_list, list){printf("遍历到节点数据: %d\n", pos->data)list_del(&pos->list)free(pos)}return 0
}

📤 输出结果

遍历到节点数据: 10  
遍历到节点数据: 20

✅ 17. list_for_each_entry_continue(pos, head, member)

📌 作用

继续从当前节点的下一个节点开始遍历。

🧾 参数
  • pos:结构体类型指针;

  • head:链表头指针;

  • member:结构体中的 list_head 成员名。


🧪 示例
// list_for_each_entry_continue_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list)int main(void)
{struct my_node *node1 = malloc(sizeof(*node1))struct my_node *node2 = malloc(sizeof(*node2))struct my_node *node3 = malloc(sizeof(*node3))node1->data = 10node2->data = 20node3->data = 30INIT_LIST_HEAD(&node1->list)INIT_LIST_HEAD(&node2->list)INIT_LIST_HEAD(&node3->list)list_add_tail(&node1->list, &my_list)list_add_tail(&node2->list, &my_list)list_add_tail(&node3->list, &my_list)struct my_node *poslist_for_each_entry(pos, &my_list, list){if (pos->data == 10)list_for_each_entry_continue(pos, &my_list, list)elseprintf("继续遍历到节点数据: %d\n", pos->data)}free(node1)free(node2)free(node3)return 0
}

📤 输出结果

继续遍历到节点数据: 20  
继续遍历到节点数据: 30

✅ 18. list_for_each_entry_from(pos, head, member)

📌 作用

从指定节点 pos 开始继续遍历链表。

🧾 参数
  • pos:结构体类型指针;

  • head:链表头指针;

  • member:结构体中的 `list_head` 成员名

🧪 示例

// list_for_each_entry_from_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list)int main(void)
{struct my_node *node1 = malloc(sizeof(*node1))struct my_node *node2 = malloc(sizeof(*node2))struct my_node *node3 = malloc(sizeof(*node3))node1->data = 10node2->data = 20node3->data = 30INIT_LIST_HEAD(&node1->list)INIT_LIST_HEAD(&node2->list)INIT_LIST_HEAD(&node3->list)list_add_tail(&node1->list, &my_list)list_add_tail(&node2->list, &my_list)list_add_tail(&node3->list, &my_list)struct my_node *poslist_for_each_entry_from(pos, &my_list, list){printf("从指定节点继续遍历到节点数据: %d\n", pos->data)}free(node1)free(node2)free(node3)return 0
}

📤 输出结果

从指定节点继续遍历到节点数据: 10  
从指定节点继续遍历到节点数据: 20  
从指定节点继续遍历到节点数据: 30

✅ 19. list_for_each_entry_safe_reverse(pos, n, head, member)

📌 作用

安全地反向遍历链表,并删除节点。

🧾 参数
  • pos:结构体类型指针;

  • n:临时指针;

  • head:链表头指针;

  • member:结构体中的 list_head 成员名。


🧪 示例
// list_for_each_entry_safe_reverse_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list)int main(void)
{struct my_node *node1 = malloc(sizeof(*node1))struct my_node *node2 = malloc(sizeof(*node2))struct my_node *node3 = malloc(sizeof(*node3))node1->data = 10node2->data = 20node3->data = 30INIT_LIST_HEAD(&node1->list)INIT_LIST_HEAD(&node2->list)INIT_LIST_HEAD(&node3->list)list_add_tail(&node1->list, &my_list)list_add_tail(&node2->list, &my_list)list_add_tail(&node3->list, &my_list)struct my_node *pos, *nlist_for_each_entry_safe_reverse(pos, n, &my_list, list){printf("倒序遍历并删除节点数据: %d\n", pos->data)list_del(&pos->list)free(pos)}return 0
}

📤 输出结果

倒序遍历并删除节点数据: 30  
倒序遍历并删除节点数据: 20  
倒序遍历并删除节点数据: 10

✅ 20. list_prepare_entry(entry, head, member)

📌 作用

用于准备遍历时的入口,在遍历开始时通过 list_for_each_entry_continue 或其他方式继续遍历。

🧾 参数
  • entry:结构体类型指针;

  • head:链表头指针;

  • member:链表结构体中的 list_head 成员名。

🧠 一句话总结 list_prepare_entry()

它的作用就是:

⚠️保证你传给 list_for_each_entry_continue() 的不是 NULL。
如果你传的是 NULL,它会自动用链表头 head 来替代,防止遍历出错。


🧃通俗比喻

想象一个队列(链表),你要从某人(比如“老王”)后面继续往后找人打招呼。

  • 如果你记得“老王”是谁(ptr != NULL),你可以从他后面开始打招呼。

  • 如果你不记得谁是“老王”了(ptr == NULL),那你就从队伍头(head)开始。

这个“准备老王或者队头”的动作,就是 list_prepare_entry() 做的事。


🧪 示例
// list_prepare_entry_example.c
#include <stdio.h>
#include <stdlib.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;
};LIST_HEAD(my_list)int main(void)
{struct my_node *node1 = malloc(sizeof(*node1))struct my_node *node2 = malloc(sizeof(*node2))struct my_node *node3 = malloc(sizeof(*node3))node1->data = 10node2->data = 20node3->data = 30INIT_LIST_HEAD(&node1->list)INIT_LIST_HEAD(&node2->list)INIT_LIST_HEAD(&node3->list)list_add_tail(&node1->list, &my_list)list_add_tail(&node2->list, &my_list)list_add_tail(&node3->list, &my_list)struct my_node *pos = NULL, *entry// 第一次遍历,查找数据为 10 的节点list_for_each_entry(entry, &my_list, list){if (entry->data == 10) {pos = entrybreak}}// 从找到的节点后开始继续遍历printf("从10之后继续遍历:\n")list_for_each_entry_continue(list_prepare_entry(pos, &my_list, list), &my_list, list){printf("遍历数据: %d\n", entry->data)}free(node1)free(node2)free(node3)return 0
}

 ✅ 输出结果:

从10之后继续遍历:
遍历数据: 20
遍历数据: 30

✅ 21. list_entry_is_head(ptr, head, member)

📌 作用

判断某个节点是否是链表的头节点。

换句话说:
它判断当前节点是否是链表头节点,在很多遍历时可以用来确认是否回到了链表头。

🧾 参数
  • ptr:链表节点指针;

  • head:链表头指针;

  • member:链表结构体中的 list_head 成员名。


🧪 示例

场景:遍历一个自定义结构体链表,打印所有节点的数据,并避免头节点被误处理。

#include <stdio.h>
#include <stdlib.h>
#include <linux/kernel.h>
#include <linux/list.h>struct my_node {int datastruct list_head list
}int main()
{struct my_node headstruct my_node node1, node2INIT_LIST_HEAD(&head.list)node1.data = 1node2.data = 2list_add(&node1.list, &head.list)list_add(&node2.list, &head.list)struct my_node *poslist_for_each_entry(pos, &head.list, list){printf("node: %d\n", pos->data)if (list_entry_is_head(pos, &head.list, list)){printf("This node is head\n")}else{printf("This node is NOT head\n")}}return 0
}

🔎 宏定义分析

#define list_entry_is_head(pos, head, member) \(&pos->member == (head))

📤 输出结果

node: 2
This node is NOT head
node: 1
This node is NOT head

✅ 示例:演示 list_entry_is_head() 返回 true

#include <stdio.h>
#include <stdlib.h>
#include <linux/kernel.h>
#include <linux/list.h>struct my_node {int datastruct list_head list
}int main()
{struct my_node head// 初始化链表头INIT_LIST_HEAD(&head.list)// 使用 list_entry() 将 list_head 转换为 my_node*struct my_node *entry = list_entry(&head.list, struct my_node, list)// 使用 list_entry_is_head() 判断if (list_entry_is_head(entry, &head.list, list)){printf("entry 是链表头节点\n")}else{printf("entry 不是链表头节点\n")}return 0
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/79274.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分书问题的递归枚举算法

分数问题的递归枚举算法 一、问题引入二、解题步骤1.问题分析思维导图2.解题步骤 三、代码实现1.代码2.复杂度分析 四、个人总结 一、问题引入 分书问题是指&#xff1a;已知 n 个人对 m 本书的喜好&#xff08;n≤m&#xff09;&#xff0c;现要将 m 本书分给 n 个人&#xf…

密码学--AES

一、实验目的 1、完成AES算法中1轮加密和解密操作 2、掌握AES的4个基本处理步骤 3、理解对称加密算法的“对称”思想 二、实验内容 1、题目内容描述 &#xff08;1&#xff09;利用C语言实现字节代换和逆向字节代换&#xff0c;字节查S盒代换 &#xff08;2&#xff09;利…

【工具记录分享】提取bilibili视频字幕

F12大法 教程很多 但方法比较统一 例快速提取视频字幕&#xff01;适用B站、AI字幕等等。好用 - 哔哩哔哩 无脑小工具 哔哩哔哩B站字幕下载_在线字幕解析-飞鱼视频下载助手 把链接扔进去就会自动生成srt文件 需要txt可以配合&#xff1a; SRT转为TXT

使用fdisk 、gdisk管理分区

用 fdisk 管理分区 fdisk 命令工具默认将磁盘划分为 mbr 格式的分区 命令&#xff1a; fdisk 设备名 fdisk 命令以交互方式进行操作的&#xff0c;在菜单中选择相应功能键即可 [rootlocalhost ~]# fdisk /dev/sda # 对 sda 进行分区 Command (m for help): # 进入 fdis…

【Linux基础】程序和软件安装管理命令

目录 install命令 which命令 install命令 作用&#xff1a;它是用于安装或复制文件到指定位置&#xff0c;并且可以同时设置文件的权限、所有者和所属组等属性。它通常用于脚本中&#xff0c;用于自动化安装程序或配置文件的部署。 基本用法&#xff1a; install [选项] 源…

C++模板梳理

目录 函数模板 类模板 变量模板 模板全特化 模板偏特化 模板显式实例化解决文件分离问题 折叠表达式 模板的二阶段编译 待决名(dependent name) SFINAE 概念与约束 函数模板 函数模板不是函数&#xff0c;只有实例化的函数模板&#xff0c;编译器才能生成实际的函数…

数据链共享:从印巴空战到工业控制的跨越性应用

摘要 本文通过对印巴空战中数据链共享发挥关键作用的分析&#xff0c;引出数据链共享在工业控制领域同样具有重大价值的观点。深入阐述 DIOS 工业控制操作系统作为工业数据链共享基础技术的特点、架构及应用优势&#xff0c;对比空战场景与工业控制场景下数据链共享的相…

巡检机器人数据处理技术的创新与实践

摘要 随着科技的飞速发展&#xff0c;巡检机器人在各行业中逐渐取代人工巡检&#xff0c;展现出高效、精准、安全等显著优势。当前&#xff0c;巡检机器人已从单纯的数据采集阶段迈向对采集数据进行深度分析的新阶段。本文探讨了巡检机器人替代人工巡检的现状及优势&#xff0c…

在 Flink + Kafka 实时数仓中,如何确保端到端的 Exactly-Once

在 Flink Kafka 构建实时数仓时&#xff0c;确保端到端的 Exactly-Once&#xff08;精确一次&#xff09; 需要从 数据消费&#xff08;Source&#xff09;、处理&#xff08;Processing&#xff09;、写入&#xff08;Sink&#xff09; 三个阶段协同设计&#xff0c;结合 Fli…

当可视化遇上 CesiumJS:突破传统,打造前沿生产配套方案

CesiumJS 技术基础介绍 CesiumJS 是一款基于 JavaScript 的开源库&#xff0c;专门用于创建动态、交互式的地理空间可视化。它利用 WebGL 技术&#xff0c;能够在网页浏览器中流畅地渲染高分辨率的三维地球和地图场景。CesiumJS 支持多种地理空间数据格式&#xff0c;包括但不…

RabbitMQ深入学习

继续上一节的学习&#xff0c;上一节学习了RabbitMQ的基本内容&#xff0c;本节学习RabbitMQ的高级特性。 RocketMQ的高级特性学习见这篇博客 目录 1.消息可靠性1.1生产者消息确认1.2消息持久化1.3消费者消息确认1.4消费失败重试机制1.5消息可靠性保证总结 2.什么是死信交换机…

Linux系统:虚拟文件系统与文件缓冲区(语言级内核级)

本节重点 初步理解一切皆文件理解文件缓冲区的分类用户级文件缓冲区与内核级文件缓冲区用户级文件缓冲区的刷新机制两级缓冲区的分层协作 一、虚拟文件系统 1.1 理解“一切皆文件” 我们都知道操作系统访问不同的外部设备&#xff08;显示器、磁盘、键盘、鼠标、网卡&#…

在c++中老是碰到string,这是什么意思?

定义一个string类型变量的引用&#xff0c;相当于给现有变量起个别名&#xff0c;与指针还是不一样的。比如string a;string& ba;这两句&#xff0c;b与a实际上是一回事&#xff0c;表示的是同一块内存。 std是系统的一个命名空间(有关命名空间可以参阅namespace_百度百科)…

Day21 奇异值分解(SVD)全面解析

一、奇异值分解概述 奇异值分解是线性代数中一个重要的矩阵分解方法&#xff0c;对于任何矩阵&#xff0c;无论是结构化数据转化成的“样本 * 特征”矩阵&#xff0c;还是天然以矩阵形式存在的图像数据&#xff0c;都能进行等价的奇异值分解&#xff08;SVD&#xff09;。 二…

akshare爬虫限制,pywencai频繁升级个人做量化,稳定数据源和券商的选择

做量化&#xff0c;数据和交易接口是策略和自动化交易的基石&#xff0c;而稳定的数据和快人一步的交易接口是个人做量化的催化剂。 之前写过一篇文章&#xff1a;个人做量化常用的数据&#xff0c;多以爬虫为主&#xff0c;最近akshare爬虫限制&#xff0c;pywencai频繁升级。…

数字签名与证书

1. 数字签名与证书 摘要算法用来实现完整性&#xff0c;能够为数据生成独一无二的“指纹”&#xff0c;常用的算法是 SHA-2&#xff1b;数字签名是私钥对摘要的加密&#xff0c;可以由公钥解密后验证&#xff0c;实现身份认证和不可否认&#xff1b;公钥的分发需要使用数字证书…

Ubuntu22.04安装显卡驱动/卸载显卡驱动

报错 今日输入nvidia-smi报错,在安装了535和550,包括560都没办法解决,但是又怕乱搞导致环境损坏,打算把显卡卸载然后重新安装系统默认推荐版本的显卡驱动 qinqin:~$ nvidia-smi Failed to initialize NVML: Driver/library version mismatch NVML library version: 560.35卸载…

Web 架构之负载均衡全解析

文章目录 一、引言二、思维导图三、负载均衡的定义与作用定义作用1. 提高可用性2. 增强性能3. 实现扩展性 四、负载均衡类型硬件负载均衡代表设备优缺点 软件负载均衡应用层负载均衡代表软件优缺点 网络层负载均衡代表软件优缺点 五、负载均衡算法轮询算法&#xff08;Round Ro…

linux下的Redis的编译安装与配置

配合做开发经常会用到redis&#xff0c;整理下编译安装配置过程&#xff0c;仅供参考&#xff01; --------------------------------------Redis的安装与配置-------------------------------------- 下载 wget https://download.redis.io/releases/redis-6.2.6.tar.gz tar…

A2A大模型协议及Java示例

A2A大模型协议概述 1. 协议作用 A2A协议旨在解决以下问题&#xff1a; 数据交换&#xff1a;不同应用程序之间的数据格式可能不一致&#xff0c;A2A协议通过定义统一的接口和数据格式解决这一问题。模型调用&#xff1a;提供标准化的接口&#xff0c;使得外部应用可以轻松调…