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.pswp.cn/web/79274.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/79274.shtml
英文地址,請注明出處:http://en.pswp.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;使得外部應用可以輕松調…