C语言函数大全-- l 开头的 Linux 内核函数(链表管理函数)

2023-11-03

本篇介绍 C语言中 l 开头的 Linux 内核函数(链表管理函数)

1. list_add,list_add_tail

1.1 函数说明

函数声明 函数功能
void list_add(struct list_head *new, struct list_head *head); 它是 Linux 内核中双向链表操作的一个基本函数,用于将新节点添加到双向链表的头部
void list_add_tail(struct list_head *new, struct list_head *head); 它是 Linux 内核中双向链表操作的一个基本函数,用于将新节点添加到链表尾部。

参数:

  • new : 要添加的新节点的指针
  • head : 链表头节点的指针。
    • list_add() 函数会将 new 节点插入到链表头之前,使其成为新的链表头节点。
    • list_add_tail() 函数会根据 链表头节点找到链表尾节点,并将 new 节点添加到链表尾部。

1.2 演示示例

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

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

struct node {
    int data;
    struct list_head link;
};

void init_list_head(struct list_head *head)
{
    head->next = head->prev = head;
}

void print_list(struct list_head *head)
{
    struct node *p;
    for (p = (struct node *)head->next; p != (struct node *)head; p = (struct node *)p->link.next) {
        printf("%d ", p->data);
    }
    printf("\n");
}

int main()
{
    struct list_head head = { NULL, NULL };
    init_list_head(&head); // 用于初始化双向链表头部节点。

    struct node *n1 = (struct node *)malloc(sizeof(struct node));
    n1->data = 1;
    list_add(&n1->link, &head); // 将节点添加到链表的头部

    struct node *n2 = (struct node *)malloc(sizeof(struct node));
    n2->data = 2;
    list_add(&n2->link, &head);

    struct node *n3 = (struct node *)malloc(sizeof(struct node));
    n3->data = 3;
    list_add_tail(&n3->link, &head); // 将节点添加到链表的尾部

    printf("The original list is: ");
    print_list(&head);

    return 0;
}

在上述示例代码中,我们首先定义了 list_headnode 两个结构体,并通过调用 init_list_head() 函数初始化链表头部。然后,我们创建了三个 node 类型的节点,前两个节点分别通过 list_add() 函数将它们添加到链表的头部,最后一个节点通过 list_add_tail() 函数添加到链表的尾部 。最后,我们调用 print_list() 函数打印链表中的元素。

注意: 在使用 list_add()list_add_tail() 函数之前,我们要为每个新节点分配内存空间。

2. list_cut_before,list_cut_position

2.1 函数说明

函数声明 函数功能
void list_cut_before(struct list_head *new, struct list_head *head, struct list_head *entry); 它是 Linux 内核中双向链表操作的一个基本函数,用于将一段节点从原始链表中移动到另一个链表中,并将其添加到新链表的头部。
void list_cut_position(struct list_head *new, struct list_head *head, struct list_head *entry); 它是 Linux 内核中双向链表操作的一个基本函数,用于将一段节点从原始链表中移动到另一个链表中,并将其添加到新链表的头部。与list_cut_before不同的是,该函数需要指定要移动的节点的具体位置,而不是直接指定一个节点。

参数:

  • new : 要添加的新链表头部;
  • head : 原始链表的头部
  • entry : 要移动的节点

list_cut_before() 函数会将 entry 节点及其前面的所有节点从原始链表中移动到 new 所指示的链表中,并将 entry 所在位置的前一个节点作为新链表的头节点。
list_cut_position() 函数会将 entry 节点及其后面的所有节点从原始链表中移动到 new 所指示的链表中,并将 entry 所在位置作为新链表的头节点。

2.2 演示示例

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

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

struct node {
    int data;
    struct list_head link;
};

void init_list_head(struct list_head *head)
{
    head->next = head->prev = head;
}

void print_list(struct list_head *head)
{
    struct node *p;
    for (p = (struct node *)head->next; p != (struct node *)head; p = (struct node *)p->link.next) {
        printf("%d ", p->data);
    }
    printf("\n");
}

int main()
{
    struct list_head head1 = { NULL, NULL };
    init_list_head(&head1);

    struct node *n1 = (struct node *)malloc(sizeof(struct node));
    n1->data = 1;
    list_add_tail(&n1->link, &head1);

    struct node *n2 = (struct node *)malloc(sizeof(struct node));
    n2->data = 2;
    list_add_tail(&n2->link, &head1);

    struct node *n3 = (struct node *)malloc(sizeof(struct node));
    n3->data = 3;
    list_add_tail(&n3->link, &head1);

    printf("The original list is: ");
    print_list(&head1);

    struct list_head head2 = { NULL, NULL };
    init_list_head(&head2);

    // 移动节点n1和n2到另一个链表中
    list_cut_before(&head2, &head1, &n2->link);
    printf("The first list after move is: ");
    print_list(&head1);
    printf("The second list after move is: ");
    print_list(&head2);

    // 再次移动节点n3到另一个链表中
    list_cut_position(&head2, &head1, &n3->link);
    printf("The first list after second move is: ");
    print_list(&head1);
    printf("The second list after second move is: ");
    print_list(&head2);

    return 0;
}

在上述示例代码中,我们首先定义了 list_headnode 两个结构体,并通过调用 init_list_head() 函数初始化两个链表的头部。然后,我们创建了三个 node 类型的节点并分别将它们添加到第一个链表的尾部。接着,我们利用list_cut_before()函数和 list_cut_position() 函数将链表中的一段节点移动到第二个链表中。最后,我们调用 print_list 函数分别打印两个链表中的元素。

3. list_del,list_del_init,list_del_init_careful

3.1 函数说明

函数声明 函数功能
void list_del(struct list_head *entry); 用于从链表中删除一个节点,但不会修改该节点的指针信息。
void list_del_init(struct list_head *entry); 用于从链表中删除一个节点,但会将被删除的节点的指针信息初始化为NULL。
void list_del_init_careful(struct list_head *entry, struct list_head *prev, struct list_head *next); 用于从链表中删除一个节点,但需要指定该节点的前驱节点和后继节点,以确保链表结构正确。

参数:

  • entry : 要删除的节点
  • prev : 该节点的前驱节点
  • next : 该节点的后继节点

3.2 演示示例

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

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

struct node {
    int data;
    struct list_head link;
};

void init_list_head(struct list_head *head)
{
    head->next = head->prev = head;
}

void print_list(struct list_head *head)
{
    struct node *p;
    for (p = (struct node *)head->next; p != (struct node *)head; p = (struct node *)p->link.next) {
        printf("%d ", p->data);
    }
    printf("\n");
}

int main()
{
    struct list_head head = { NULL, NULL };
    init_list_head(&head);

    struct node *n1 = (struct node *)malloc(sizeof(struct node));
    n1->data = 1;
    list_add_tail(&n1->link, &head);

    struct node *n2 = (struct node *)malloc(sizeof(struct node));
    n2->data = 2;
    list_add_tail(&n2->link, &head);

    struct node *n3 = (struct node *)malloc(sizeof(struct node));
    n3->data = 3;
    list_add_tail(&n3->link, &head);

    printf("The original list is: ");
    print_list(&head);

    // 删除节点n2,但不改变其指针信息
    list_del(&n2->link);
    printf("The list after delete n2 is: ");
    print_list(&head);

    // 删除节点n3,并初始化其指针信息为NULL
    list_del_init(&n3->link);
    printf("The list after delete and init n3 is: ");
    print_list(&head);

    // 删除节点n1,并指定其前驱和后继节点
    list_del_init_careful(&n1->link, &head, head.next);
    printf("The list after careful delete n1 is: ");
    print_list(&head);

    return 0;
}

在上述示例代码中,我们首先定义了 list_headnode 两个结构体,并通过调用 init_list_head() 函数初始化链表头部。然后,我们创建了三个 node 类型的节点并分别将它们添加到链表的尾部。接下来,我们利用 list_del()list_del_init()list_del_init_careful() 函数从链表中删除节点,并打印每次操作后的链表元素。

注意: 在使用这些函数之前,我们要确保被删除的节点在链表中。

4. list_empty,list_empty_careful

4.1 函数说明

函数声明 函数功能
int list_empty(const struct list_head *head); 用于判断链表是否为空,并返回非零值表示为空,返回0表示不为空
int list_empty_careful(const struct list_head *head); 用于判断链表是否为空,但会先检查链表头部的指针是否为空,以避免对空指针进行解引用。

参数:

  • head : 要判断的链表头部

4.2 演示示例

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

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

struct node {
    int data;
    struct list_head link;
};

void init_list_head(struct list_head *head)
{
    head->next = head->prev = head;
}

void print_list(struct list_head *head)
{
    struct node *p;
    for (p = (struct node *)head->next; p != (struct node *)head; p = (struct node *)p->link.next) {
        printf("%d ", p->data);
    }
    printf("\n");
}

int main()
{
    struct list_head head = { NULL, NULL };
    init_list_head(&head);

    printf("Is the list empty? %d\n", list_empty(&head));
    printf("Is the list empty carefully? %d\n", list_empty_careful(&head));

    struct node *n1 = (struct node *)malloc(sizeof(struct node));
    n1->data = 1;
    list_add_tail(&n1->link, &head);

    printf("The list after adding n1: ");
    print_list(&head);
    printf("Is the list empty? %d\n", list_empty(&head));
    printf("Is the list empty carefully? %d\n", list_empty_careful(&head));

    struct node *n2 = (struct node *)malloc(sizeof(struct node));
    n2->data = 2;
    list_add_tail(&n2->link, &head);

    printf("The list after adding n2: ");
    print_list(&head);
    printf("Is the list empty? %d\n", list_empty(&head));
    printf("Is the list empty carefully? %d\n", list_empty_careful(&head));

    return 0;
}

在上述示例代码中,我们首先定义了 list_headnode 两个结构体,并通过调用init_list_head()函数初始化链表头部。然后,我们利用list_empty()和list_empty_careful()函数分别判断链表是否为空,并打印其返回值。接下来,我们创建了两个node类型的节点并分别将它们添加到链表的尾部。每次添加节点后,我们再次使用list_empty和list_empty_careful函数判断链表是否为空,并打印其返回值。

需要注意的是,在使用这些函数之前,我们要确保链表头部已经初始化。

5. Linux 内核中双向链表遍历相关宏

宏定义 宏描述
#define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) 用于获取一个节点所在结构体的起始地址。
static inline int list_entry_is_head(const struct list_head *entry, const struct list_head *head) { return entry->prev == head; } 用于判断给定节点是否为链表头。
#define list_first_entry(ptr, type, member) list_entry((ptr)->next, type, member) 用于获取链表中第一个节点所在结构体的起始地址。
#define list_first_entry_or_null(ptr, type, member) ({ struct list_head *__head = (ptr); struct list_head *__pos = __head->next; __pos != __head ? list_entry(__pos, type, member) : NULL; }) 用于获取链表中第一个节点所在结构体的起始地址,但会先检查链表是否为空,以避免对空指针进行解引用。
#define list_next_entry(pos, member) list_entry((pos)->member.next, typeof(*(pos)), member) 用于获取链表中紧随给定节点之后的节点所在结构体的起始地址。
#define list_last_entry(ptr, type, member) list_entry((ptr)->prev, type, member) 用于获取链表中最后一个节点所在结构体的起始地址。
#define list_prepare_entry(pos, ptr, member) ((pos) ? : list_entry(ptr, typeof(*pos), member)) 用于准备一个节点的数据结构指针。如果该指针为NULL,则将其初始化为链表的头部。
#define list_prev_entry(pos, member) list_entry((pos)->member.prev, typeof(*(pos)), member) 用于获取链表中紧靠给定节点之前的节点所在结构体的起始地址。
#define list_for_each(pos, head) for (pos = (head)->next; pos != (head); pos = pos->next) 遍历链表中的所有节点
#define list_for_each_continue(pos, head) for (pos = pos->next; pos != (head); pos = pos->next) 从当前节点继续遍历链表中的剩余节点。
#define list_for_each_prev(pos, head) for (pos = (head)->prev; pos != (head); pos = pos->prev) 从链表尾部开始遍历所有节点。
#define list_for_each_safe(pos, n, head) for (pos = (head)->next, n = pos->next; pos != (head); pos = n, n = pos->next) 与list_for_each函数类似,但允许在遍历过程中删除或添加节点。其中,n参数表示要处理的下一个节点。
#define list_for_each_prev_safe(pos, n, head) for (pos = (head)->prev, n = pos->prev; pos != (head); pos = n, n = pos->prev) 与list_for_each_safe函数类似,但遍历顺序是从链表尾部开始。
#define list_for_each_entry(pos, head, member) for (pos = list_first_entry(head, typeof(*pos), member); &pos->member != (head); pos = list_next_entry(pos, member)) 用于在遍历链表时,获取每个节点所在结构体的起始地址。其中,pos参数表示当前节点所在结构体的指针;head参数表示要遍历的链表头部指针;member参数表示每个节点在结构体中的成员名称。
#define list_for_each_entry_reverse(pos, head, member) for (pos = list_last_entry(head, typeof(*pos), member); &pos->member != (head); pos = list_prev_entry(pos, member)) 与list_for_each_entry函数类似,但遍历顺序是从链表尾部开始。
#define list_for_each_entry_continue(pos, head, member) for (pos = list_next_entry(pos, member); &pos->member != (head); pos = list_next_entry(pos, member)) 用于从当前节点继续往后遍历链表,并获取每个节点所在结构体的起始地址。
#define list_for_each_entry_continue_reverse(pos, head, member) for (pos = list_prev_entry(pos, member); &pos->member != (head); pos = list_prev_entry(pos, member)) 与list_for_each_entry_continue函数类似,但遍历顺序是从链表尾部开始。
#define list_for_each_entry_from(pos, head, member) for (; &pos->member != (head); pos = list_next_entry(pos, member)) 用于从某个节点开始遍历链表,并获取每个节点所在结构体的起始地址。其中,pos参数表示当前要遍历的节点所在结构体的指针;head参数表示要遍历的链表头部指针;member参数表示每个节点在结构体中的成员名称。
#define list_for_each_entry_from_reverse(pos, head, member) for (; &pos->member != (head); pos = list_prev_entry(pos, member)) 与list_for_each_entry_from函数类似,但遍历顺序是从链表尾部开始。
#define list_for_each_entry_safe(pos, n, head, member) for (pos = list_first_entry(head, typeof(*pos), member), n = list_next_entry(pos, member); &pos->member != (head); pos = n, n = list_next_entry(n, member)) 与list_for_each_entry函数类似,但允许在遍历过程中删除或添加节点。其中,n参数表示要处理的下一个节点。
#define list_for_each_entry_safe_continue(pos, n, head, member) for (pos = list_next_entry(pos, member), n = list_next_entry(pos, member); &pos->member != (head); pos = n, n = list_next_entry(n, member)) 用于从当前节点继续往后遍历链表,并允许在遍历过程中删除或添加节点。
#define list_for_each_entry_safe_from(pos, n, head, member) for (n = list_next_entry(pos, member); &pos->member != (head); pos = n, n = list_next_entry(n, member)) 用于从某个节点开始遍历链表,并允许在遍历过程中删除或添加节点。
#define list_for_each_entry_safe_reverse(pos, n, head, member) for (pos = list_last_entry(head, typeof(*pos), member), n = list_prev_entry(pos, member); &pos->member != (head); pos = n, n = list_prev_entry(n, member)) 与list_for_each_entry_reverse函数类似,但允许在遍历过程中删除或添加节点。
#define list_is_first(pos, head) ((pos)->prev == (head)) 用于检查当前节点是否为链表中的第一个节点。其中,pos 参数表示要检查的节点指针;head 参数表示链表头部指针。
#define list_is_last(pos, head) ((pos)->next == (head)) 用于检查当前节点是否为链表中的最后一个节点。其中,pos 参数表示要检查的节点指针;head 参数表示链表头部指针。
#define list_is_head(pos, head) ((pos) == (head)) 用于检查当前节点是否为链表头部。其中,pos参数表示要检查的节点指针;head参数表示链表头部指针。
#define list_is_singular(head) (!list_empty(head) && ((head)->next == (head)->prev)) 用于检查链表中是否只有一个节点。其中,head参数表示链表头部指针。
#define list_safe_reset_next(curr, next, member) next = list_entry((curr)->member.next, typeof(*curr), member) 用于安全地重置一个节点的后继节点指针,以便在遍历链表时删除当前节点。其中,curr 参数表示当前节点指针;next 参数表示当前节点的后继节点指针;member 参数表示节点结构体中 struct list_head 成员的名称。

6. list_move,list_move_tail,list_bulk_move_tail

6.1 函数说明

函数声明 函数功能
void list_move(struct list_head *list, struct list_head *head); 用于将一个节点移动到另外一个链表的头部。
void list_move_tail(struct list_head *list, struct list_head *head); 用于将一个节点移动到另外一个链表的尾部。
void list_bulk_move_tail(struct list_head *list, int count, struct list_head *head); 用于将多个节点从一个链表移动到另一个链表的尾部。

参数:

  • list : 要移动的节点指针
  • head : 目标链表头部指针
  • count : 要移动的节点数量;

6.2 演示示例

#include <stdio.h>
#include <stdlib.h>
#include "list.h"

struct my_struct {
    int data;
    struct list_head list;
};

int main() {
    struct list_head a, b;
    struct my_struct s1, s2, s3, *pos, *tmp;

    // 初始化两个链表
    INIT_LIST_HEAD(&a);
    INIT_LIST_HEAD(&b);

    // 添加三个结构体到链表 a 中
    s1.data = 10;
    list_add_tail(&s1.list, &a);

    s2.data = 20;
    list_add_tail(&s2.list, &a);

    s3.data = 30;
    list_add_tail(&s3.list, &a);

    // 将节点 s1 移动到链表 b 的头部
    printf("Before move:\n");
    printf("List a: ");
    list_for_each_entry(pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    printf("List b: ");
    list_for_each_entry(pos, &b, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    list_move(&s1.list, &b);

    printf("After move:\n");
    printf("List a: ");
    list_for_each_entry(pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    printf("List b: ");
    list_for_each_entry(pos, &b, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    // 将节点 s2 移动到链表 b 的尾部
    printf("Before move_tail:\n");
    printf("List a: ");
    list_for_each_entry(pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    printf("List b: ");
    list_for_each_entry(pos, &b, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    list_move_tail(&s2.list, &b);

    printf("After move_tail:\n");
    printf("List a: ");
    list_for_each_entry(pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    printf("List b: ");
    list_for_each_entry(pos, &b, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    // 将链表 a 中的所有节点移动到链表 b 的尾部
    printf("Before bulk_move_tail:\n");
    printf("List a: ");
    list_for_each_entry(pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    printf("List b: ");
    list_for_each_entry(pos, &b, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    list_bulk_move_tail(&a, 3, &b);

    printf("After bulk_move_tail:\n");
    printf("List a: ");
    list_for_each_entry(pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    printf("List b: ");
    list_for_each_entry(pos, &b, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    // 释放内存
    list_for_each_entry_safe(pos, tmp, &a, list) {
        list_del(&pos->list);
        free(pos);
    }

    list_for_each_entry_safe(pos, tmp, &b, list) {
        list_del(&pos->list);
        free(pos);
    }

    return 0;
}

上述示例代码中,我们首先创建了两个链表 ab,然后向链表 a 中添加三个结构体。接着,我们使用 list_move() 函数将节点 s1 从链表 a 移动到链表 b 的头部,使用 list_move_tail() 函数将节点 s2 从链表 a 移动到链表 b 的尾部,最后使用 list_bulk_move_tail() 函数将链表 a 中的所有节点都移动到链表 b 的尾部。

注意: 在上述演示代码的最后,我们需要手动释放所有节点的内存空间,以免造成内存泄漏。

7. list_replace,list_replace_init

7.1 函数说明

函数声明 函数功能
void list_replace(struct list_head *old, struct list_head *new); 用于用一个新节点替换指定节点。
void list_replace_init(struct list_head *old, struct list_head *new); 除了可以完成 list_replace 做的所有操作外,它还将原来的节点初始化为空。

参数:

  • old : 要被替换的节点指针;
  • new : 新节点的指针。

7.2 演示示例

#include <stdio.h>
#include "list.h"

struct my_struct {
    int data;
    struct list_head list;
};

int main() {
    struct list_head a;
    struct my_struct s1, s2, s3, s4;

    // 初始化链表
    INIT_LIST_HEAD(&a);

    // 添加三个结构体到链表中
    s1.data = 10;
    list_add_tail(&s1.list, &a);

    s2.data = 20;
    list_add_tail(&s2.list, &a);

    s3.data = 30;
    list_add_tail(&s3.list, &a);

    printf("Before replace:\n");
    list_for_each_entry(struct my_struct, pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    // 替换第二个节点
    s4.data = 40;
    list_replace(&s2.list, &s4.list);

    printf("After replace:\n");
    list_for_each_entry(struct my_struct, pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    // 替换第一个节点,并且清空原来的节点
    s4.data = 50;
    list_replace_init(&s1.list, &s4.list);

    printf("After replace_init:\n");
    list_for_each_entry(struct my_struct, pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    // 释放内存
    list_del(&s3.list);
    list_del(&s4.list);

    return 0;
}

在上面的示例代码中,我们首先创建了一个链表 a,然后向其中添加三个结构体。接着,我们使用 list_replace() 函数将第二个节点 s2 替换成新节点 s4,并打印出替换后的链表元素;然后,我们使用 list_replace_init() 函数将第一个节点 s1 替换成新节点 s4,并清空原来的节点,同样打印出替换后的链表元素。

8. list_rotate_left,list_rotate_to_front

8.1 函数说明

函数声明 函数功能
static inline void list_rotate_left(struct list_head *head) 用于将链表向左旋转一个位置。
void list_rotate_to_front(struct list_head *head, struct list_head *pivot); 用于将指定节点移到链表头部,并旋转链表使得该节点成为新的头部。

参数:

  • head : 链表头部指针
  • pivot 要移到链表头部的节点指针。

8.2 演示示例

#include <stdio.h>
#include "list.h"

struct my_struct {
    int data;
    struct list_head list;
};

int main() {
    struct list_head a;
    struct my_struct s1, s2, s3;

    // 初始化链表
    INIT_LIST_HEAD(&a);

    // 添加三个结构体到链表中
    s1.data = 10;
    list_add_tail(&s1.list, &a);

    s2.data = 20;
    list_add_tail(&s2.list, &a);

    s3.data = 30;
    list_add_tail(&s3.list, &a);

    printf("Before rotate:\n");
    list_for_each_entry(struct my_struct, pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    // 将第二个节点移到链表头部,并旋转链表
    list_rotate_to_front(&a, &s2.list);

    printf("After rotate:\n");
    list_for_each_entry(struct my_struct, pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    // 向左旋转一个位置
    list_rotate_left(&a);

    printf("After rotate_left:\n");
    list_for_each_entry(struct my_struct, pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    // 释放内存
    list_del(&s1.list);
    list_del(&s2.list);
    list_del(&s3.list);

    return 0;
}

在上面的示例代码中,我们首先创建了一个链表 a,然后向其中添加三个结构体。接着,我们使用 list_rotate_to_front() 函数将第二个节点 s2 移到链表头部并旋转链表,打印出操作后的链表元素;然后,我们使用 list_rotate_left() 函数将链表向左旋转一个位置,同样打印出操作后的链表元素。

9. list_splice,list_splice_tail,list_splice_init,list_splice_tail_init

9.1 函数说明

函数声明 函数功能
void list_splice(struct list_head *list, struct list_head *head); 用于将一个链表中的所有节点插入到另一个链表的指定位置之前。
void list_splice_tail(struct list_head *list, struct list_head *head); 用于将一个链表中的所有节点插入到另一个链表的尾部。
void list_splice_init(struct list_head *list, struct list_head *head); 除了可以完成 list_splice 做的所有操作外,它还将原来的链表初始化为空。
void list_splice_tail_init(struct list_head *list, struct list_head *head); 除了可以完成 list_splice_tail 做的所有操作外,它还将原来的链表初始化为空。

参数:

  • list : 要插入的链表头部指针
  • head :
    • list_splice()list_splice_init() 中表示目标链表插入的位置
    • list_splice_tail()list_splice_tail_init() 中表示目标链表尾部的前一个节点

9.2 演示示例

#include <stdio.h>
#include "list.h"

struct my_struct {
    int data;
    struct list_head list;
};

int main() {
    struct list_head a, b;
    struct my_struct s1, s2, s3, s4, s5;

    // 初始化两个链表
    INIT_LIST_HEAD(&a);
    INIT_LIST_HEAD(&b);

    // 向链表 a 中添加三个结构体
    s1.data = 10;
    list_add_tail(&s1.list, &a);

    s2.data = 20;
    list_add_tail(&s2.list, &a);

    s3.data = 30;
    list_add_tail(&s3.list, &a);

    // 向链表 b 中添加两个结构体
    s4.data = 40;
    list_add_tail(&s4.list, &b);

    s5.data = 50;
    list_add_tail(&s5.list, &b);

    printf("Before splice:\n");
    printf("List a:\n");
    list_for_each_entry(struct my_struct, pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\nList b:\n");
    list_for_each_entry(struct my_struct, pos, &b, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    // 将链表 b 中的所有节点插入到链表 a 的头部
    list_splice(&b, &a);

    printf("After splice:\n");
    printf("List a:\n");
    list_for_each_entry(struct my_struct, pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\nList b:\n");
    list_for_each_entry(struct my_struct, pos, &b, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    // 将链表 b 中的所有节点插入到链表 a 的尾部
    list_splice_tail(&b, &a);

    printf("After splice tail:\n");
    printf("List a:\n");
    list_for_each_entry(struct my_struct, pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\nList b:\n");
    list_for_each_entry(struct my_struct, pos, &b, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    // 重新初始化链表 a 并将链表 b 中的所有节点插入到链表 a 的头部
    INIT_LIST_HEAD(&a);
    list_splice_init(&b, &a);

    printf("After splice init:\n");
    printf("List a:\n");
    list_for_each_entry(struct my_struct, pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\nList b:\n");
    list_for_each_entry(struct my_struct, pos, &b, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    // 将链表 b 中的所有节点插入到链表 a 的尾部,并初始化链表 b
    INIT_LIST_HEAD(&b);
    list_splice_tail_init(&a, &b);

    printf("After splice tail init:\n");
    printf("List a:\n");
    list_for_each_entry(struct my_struct, pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\nList b:\n");
    list_for_each_entry(struct my_struct, pos, &b, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    return 0;
}

上述演示代码中,我们创建了两个链表 ab,并初始化为空。然后,我们向链表 a 中添加三个结构体,向链表 b 中添加两个结构体,并使用 list_for_each_entry 宏分别遍历两个链表并输出节点数据。

接着,我们使用 list_splice() 函数将链表 b 中的所有节点插入到链表 a 的头部,使用 list_splice_tail() 函数将链表 b 中的所有节点插入到链表 a 的尾部,并使用 list_for_each_entry 宏再次遍历两个链表并输出节点数据,可以看到链表 a 中包含了链表 b 中的所有节点。

接下来,我们使用 INIT_LIST_HEAD 宏重新初始化链表 a 并使用 list_splice_init() 函数将链表 b 中的所有节点插入到链表 a 的头部,使用 INIT_LIST_HEAD 宏重新初始化链表 b 并使用 list_splice_tail_init() 函数将链表 a 中的所有节点插入到链表 b 的尾部,并使用 list_for_each_entry 宏再次遍历两个链表并输出节点数据,可以看到两个链表中的节点顺序已经被重新排列。

10. list_swap

10.1 函数说明

函数声明 函数功能
void list_swap(struct list_head *list1, struct list_head *list2); 交换两个链表头部的位置。

参数:
list1list2 分别指向两个要交换头部的链表。

10.2 演示示例

#include <stdio.h>
#include "list.h"

struct my_struct {
    int data;
    struct list_head list;
};

int main() {
    struct list_head a, b;
    struct my_struct s1, s2, s3, s4;

    // 初始化两个链表
    INIT_LIST_HEAD(&a);
    INIT_LIST_HEAD(&b);

    // 向链表 a 中添加三个结构体
    s1.data = 10;
    list_add_tail(&s1.list, &a);

    s2.data = 20;
    list_add_tail(&s2.list, &a);

    s3.data = 30;
    list_add_tail(&s3.list, &a);

    // 向链表 b 中添加一个结构体
    s4.data = 40;
    list_add_tail(&s4.list, &b);

    printf("Before swap:\n");
    printf("List a:\n");
    list_for_each_entry(struct my_struct, pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\nList b:\n");
    list_for_each_entry(struct my_struct, pos, &b, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    // 交换链表 a 和链表 b 的头部
    list_swap(&a, &b);

    printf("After swap:\n");
    printf("List a:\n");
    list_for_each_entry(struct my_struct, pos, &a, list) {
        printf("%d ", pos->data);
    }
    printf("\nList b:\n");
    list_for_each_entry(struct my_struct, pos, &b, list) {
        printf("%d ", pos->data);
    }
    printf("\n");

    return 0;
}

在上述示例中,我们创建了两个链表 ab,并向链表 a 中添加三个节点,向链表 b 中添加一个节点。然后我们使用 list_for_each_entry 宏遍历两个链表并输出节点数据。

接着,我们使用 list_swap() 函数交换链表 a 和链表 b 的头部,并使用 list_for_each_entry 宏再次遍历两个链表并输出节点数据,可以看到链表 a 的头部变成了原来的链表 b 的头部,链表 b 的头部变成了原来的链表 a 的头部。

参考

  1. [The Linux Kernel API]
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言函数大全-- l 开头的 Linux 内核函数(链表管理函数) 的相关文章

  • C语言函数大全-- w 开头的函数(1)

    w 开头的函数 1 1 wcscat 1 1 函数说明 1 2 演示示例 1 3 运行结果 2 wcschr 2 1 函数说明 2 2 演示示例 2 3 运行结果 3 wcscmp 3 1 函数说明 3 2 演示示例 3 3 运行结果 4
  • C语言函数大全-- x 开头的函数(3)

    x 开头的函数 3 1 xdr opaque 1 1 函数说明 1 2 演示示例 2 xdr opaque auth 2 1 函数说明 2 2 演示示例 2 2 1 opaque auth 2 2 2 test c 3 xdr pointe
  • C语言函数大全-- l 开头的函数

    l 开头的函数 1 labs llabs 1 1 函数说明 1 2 演示示例 1 3 运行结果 2 ldexp ldexpf ldexpl 2 1 函数说明 2 2 演示示例 2 3 运行结果 3 ldiv lldiv 3 1 函数说明 3
  • C语言函数大全-- p 开头的函数

    p 开头的函数 1 perror 1 1 函数说明 1 2 演示示例 1 3 运行结果 2 pieslice 2 1 函数说明 2 2 演示示例 2 3 运行结果 3 pow powf powl 3 1 函数说明 3 2 演示示例 3 3
  • C语言函数大全-- l 开头的 Linux 内核函数(链表管理函数)

    l 开头的 Linux 内核函数 链表管理函数 1 list add list add tail 1 1 函数说明 1 2 演示示例 2 list cut before list cut position 2 1 函数说明 2 2 演示示例
  • C语言函数大全-- s 开头的函数(3)

    s 开头的函数 3 1 sleep 1 1 函数说明 1 2 演示示例 1 3 运行结果 2 sopen 2 1 函数说明 2 2 演示示例 2 3 运行结果 3 sound 3 1 函数说明 3 2 演示示例 4 spawnl 4 1 函
  • C语言函数大全-- s 开头的函数(2)

    s 开头的函数 2 1 setlinestyle 1 1 函数说明 1 2 演示示例 1 3 运行结果 2 setmem 2 1 函数说明 2 2 演示示例 3 setmode 3 1 函数说明 3 2 演示示例 3 3 运行结果 4 se
  • C语言函数大全-- z 开头的函数

    z 开头的函数 1 zalloc 1 1 函数说明 1 2 演示示例 2 zcalloc 2 1 函数说明 2 2 演示示例 3 zcfree 3 1 函数说明 3 2 演示示例 4 zclearerr 4 1 函数说明 4 2 演示示例
  • C语言函数大全-- x 开头的函数(5)

    x 开头的函数 5 1 xdrmem create 1 1 函数说明 1 2 演示示例 2 xdrmem destroy 2 1 函数说明 2 2 演示示例 3 xdrrec create 3 1 函数说明 3 2 演示示例 4 xdrre
  • C语言函数大全-- u 开头的函数

    u 开头的函数 1 ultoa 1 1 函数说明 1 2 演示示例 1 3 运行结果 2 ungetc 2 1 函数说明 2 2 演示示例 2 3 运行结果 3 ungetch 3 1 函数说明 3 2 演示示例 3 3 运行结果 4 un
  • C语言函数大全-- t 开头的函数

    t 开头的函数 1 tan tanf tanl 1 1 函数说明 1 2 演示示例 1 3 运行结果 2 tanh tanhf tanhl 2 1 函数说明 2 2 演示示例 2 3 运行结果 3 tell 3 1 函数说明 3 2 演示示
  • C语言函数大全-- v 开头的函数

    v 开头的函数 1 va start 1 1 函数说明 1 2 演示示例 1 3 运行结果 2 va arg 2 1 函数说明 2 2 演示示例 3 va copy 3 1 函数说明 3 2 演示示例 4 va end 4 1 函数说明 4
  • C语言函数大全--e开头的函数

    e开头的函数 1 ecvt 1 1 函数说明 1 2 演示示例 1 3 运行结果 2 ellipse 2 1 函数说明 2 2 演示示例 2 3 运行结果 3 eof 3 1 函数说明 3 2 演示示例 3 3 运行结果 4 execl 4
  • C语言函数大全--h开头的函数

    h开头的函数或宏 1 hypot hypotf hypotl 1 1 函数说明 1 2 演示示例 1 3 运行结果 2 HUGE VAL HUGE VALF HUGE VALL 2 1 函数说明 2 2 演示示例 2 3 运行结果 3 ha
  • C语言函数大全-- x 开头的函数(1)

    x 开头的函数 1 1 xdr accepted reply 1 1 函数说明 1 2 演示示例 1 2 1 accepted reply 1 2 2 Test c 2 xdr array 2 1 函数说明 2 2 演示示例 3 xdr a
  • C语言函数大全-- k 开头的函数

    k 开头的函数 1 kcalloc 1 1 函数说明 1 2 演示示例 2 kbhit 2 1 函数说明 2 2 演示示例 2 3 运行结果 3 keep 3 1 函数说明 3 2 演示示例 4 kernel thread 4 1 函数说明
  • C语言函数大全-- r 开头的函数

    r 开头的函数 1 raise 1 1 函数说明 1 2 演示示例 1 3 运行结果 2 rand 2 1 函数说明 2 2 演示示例 2 3 运行结果 3 read 3 1 函数说明 3 2 演示示例 3 3 运行结果 4 realloc
  • C语言函数大全-- y 开头的函数

    y 开头的函数 1 yperror 1 1 函数说明 1 2 演示示例 2 yp match 2 1 函数说明 2 2 演示示例 3 y0 零阶第二类贝塞尔函数 3 1 函数说明 3 2 演示示例 3 3 运行结果 4 y1 一阶第二类贝塞
  • C语言函数大全--f开头的函数(下)

    f开头的函数 下 21 floor floorf floorl 21 1 函数说明 21 2 演示示例 21 3 运行结果 22 flushall 22 1 函数说明 22 2 演示示例 22 3 运行结果 23 fma fmaf fmal
  • C语言函数大全-- x 开头的函数(4)

    x 开头的函数 4 1 xdr struct 1 1 函数说明 1 2 演示示例 2 xdr u char 2 1 函数说明 2 2 演示示例 3 xdr u hyper 3 1 函数说明 3 2 演示示例 4 xdr u int 4 1

随机推荐

  • 利用ipv6,在windows和ipad上远程访问共享文件夹

    利用ipv6 在windows和ipad上远程访问共享文件夹 背景 之前说到用ipv6 解决了远程桌面连接问题 利用ipv6远程桌面 彻底解决校园网掉线带来的问题 那么问题又来了 我在实验室电脑下载了python学习视频 这个视频特别大 几
  • 帧间差分法、背景减法、光流场法简介

    概述 运动目标检测是指当监控场景中有活动目标时 采用图像分割的方法从背景图像中提取出目标的运动区域 运动目标检测技术是智能视频分析的基础 因为目标跟踪 行为理解等视频分析算法都是针对目标区域的像素点进行的 目标检测的结果直接决定着智能视觉监
  • 4个最实用最强大ChatGPT插件

    GPT的插件有很多 功能也很强大 这些插件是自定义模块 可以集成到为特定行业量身定制的 AI 聊天机器人中 包括电子商务 医疗保健 金融和教育 使用 ChatGPT 插件 您现在可以做的不仅仅是聊天 今天给大家分享4个经常使用的插件 希望能
  • Qt Plugin插件机制与实例

    1 Qt 插件机制 1 1 Qt 插件简介 插件是一种遵循一定规范的应用程序接口编写出来的程序 定位于开发实现应用软件平台不具备的功能的程序 插件与宿主程序之间通过接口联系 就像硬件插卡一样 可以被随时删除 插入和修改 所以结构很灵活 容易
  • 深度:微软对Sun的步步紧逼催生了JavaFX

    在旧金山召开的JavaOne会议上 Sun首次公开了Java家族的新产品JavaFX 之后国内外各大媒体就开始竞相报道 也采访了国内的一些Java技术方面的专家 大家针对这一产品的褒贬不一 打开参与JavaFX产品研发的Sun的工程师Chr
  • 解决一个安装office2016缺少vcruntime140.dll的问题

    问题描述 在安装并激活好office2016之后 双击word 显示 缺少vcruntime140 dll 解决方案一 1 下载vcruntime140 dll 2 复制到C盘windows sysWOW64 3 然后寻找下载文件夹里zhu
  • 一文详解 Spring Bean 循环依赖

    一 背景 有好几次线上发布老应用时 遇到代码启动报错 具体错误如下 Caused by org springframework beans factory BeanCurrentlyInCreationException Error cre
  • chooseAddress:fail the api need to be declared in the requiredPrivateInfos field in app.json/ext.js

    错误描述 在使用uni app开发微信小程序的时候 想要通过uni chooseLocation获取用户地理位置的时候出现chooseAddress fail the api need to be declared in the requi
  • Linux环境安装配置ffmpeg

    最近需要在云主机上配置ffmpeg 租的服务器上面的环境往往是Linux 参考别人的文章配好了环境 在此进行综合记录 参考文章 https zhuanlan zhihu com p 347780238 https blog csdn net
  • Python 变量类型

    变量是存储在内存中的值 这就意味着在创建变量时会在内存中开辟一个空间 基于变量的数据类型 解释器会分配指定内存 并决定什么数据可以被存储在内存中 因此 变量可以指定不同的数据类型 这些变量可以存储整数 小数或字符 变量赋值 Python 中
  • python技能描述_【python】利用python爬虫 将LOL所有英雄的技能介绍给爬取下来

    工欲善其事 必先利其器 要想玩好LOL 那了解所有英雄的技能必然是其最基本的 所以此爬虫就应运而生 运行环境 python 3 7 此爬虫所用的库有 requests 获取网页信息 openpyxl Excel相关操作 pymysql My
  • UE4 命令行创建Pak

    原创文章 转载请注明出处 回头还会出一个通过编辑器扩展创建Pak的 命令行的还是比较麻烦 命令行打包如下 引擎版本4 25 由于使用新的引擎版本 感觉pak这块变化挺大的 1 gt 注意中间的空格 2 gt 解析 1 E engine 4
  • 2021全国大学生信息安全竞赛初赛部分WP

    第一阶段 Misc tiny traffic 分析pcap包 能发现几个可疑的请求 flag wrapper test secret 分别把它们传输的文件提取出来 得到三个压缩包 通过flag gzip的内容可以确认这个IP就是我们要分析的
  • 初次使用PPYOLOE-R

    目的 优化基于yolov5 obb旋转目标检测算法的证件区域检测 之前的方法是基于anchor 每次使用都要调试anchor 而ppyoloe r是free anchor的算法 源码位置 https github com PaddlePad
  • 网络IO的阻塞与非阻塞、异步与同步

    1 阻塞与非阻塞 IO默认是阻塞的 设置非阻塞方法如下 include
  • Oracle自动工作量资料档案库(AWR)和自动数据库诊断监视器(ADDM)基础知识

    OracleDB内置的AWR存放一些快照 这些快照的内容是重要的统计信息和工作量的信息 默认每隔60分钟自动从SGA中获取一次 被易管理性监视器 MMOM 存在磁盘上 默认保存8天 可以在EM中单击 Server 服务器 选项卡 然后单击
  • 【PI控制】位置式PI的拉普拉斯变化和离散化(在开关电源的应用)

    0 导读 开关电源主要是利用MCU的PWM波控制MOS管导通与截止 以达到控制输出电压或则输出电流的作用 以下描述均建立在开关电源控制上 1 位置式PI 位置式PI控制式对于咱这种小白是一种比较友善和直观的控制算法 很形象的描述了对控制对象
  • C# 序列化原码分析

    mcs class referencesource mscorlib system runtime serialization formatterservices cs private static MemberInfo GetSerial
  • 华为OD机试真题【滑动窗口最大值】Java版本

    滑动窗口最大值 题目描述 有一个N个整数的数组 和一个长度为M的窗口 窗口从数组内的第一个数开始滑动直到窗口不能滑动为止 每次窗口滑动产生一个窗口和 窗口内所有数和和 求窗口滑动产生的所有窗口和的最大值 输入描述 第一行输入一个正整数N 表
  • C语言函数大全-- l 开头的 Linux 内核函数(链表管理函数)

    l 开头的 Linux 内核函数 链表管理函数 1 list add list add tail 1 1 函数说明 1 2 演示示例 2 list cut before list cut position 2 1 函数说明 2 2 演示示例