内核链表及其操作

内核链表的特点:

  1. 双向链表:每个节点都包含指向前驱节点和后继节点的指针,使得在链表中进行插入、删除、查找等操作非常高效。

  2. 嵌入式设计:内核链表的一个显著特点是链表节点通常嵌入在结构体中,通过 list_head 结构体实现。这样设计的好处是,可以让任意结构体通过 list_head 成为链表的一部分。

  3. 高效操作:内核链表支持 O(1) 复杂度的插入和删除操作,非常适合处理大量数据结构的场景。

  4. 安全性:内核链表在删除元素时会设置 prevnext 指针为空,防止误操作。

 

核心数据结构:

内核链表主要通过以下两个数据结构来实现:

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

 

这个结构体用于保存链表的两个指针,分别指向下一个和上一个节点。

常用操作:

1. 定义链表结构

定义一个双向链表的节点结构,类似于内核中的 list_head,包含两个指针,分别指向前驱节点和后继节点。此外,还可以嵌入一些实际的数据。

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

// 定义链表节点结构
struct my_list_head {
    struct my_list_head *prev;  // 前驱节点
    struct my_list_head *next;  // 后继节点
};

// 数据节点结构,包含数据和链表指针
struct my_node {
    int data;
    struct my_list_head list;  // 嵌入链表指针
};

2. 初始化链表头

链表头是一个特殊节点,指向链表的第一个和最后一个节点。我们可以用一个宏来简化链表的初始化操作。

// 初始化链表头宏
#define INIT_LIST_HEAD(ptr) do { \
    (ptr)->next = (ptr); \
    (ptr)->prev = (ptr); \
} while (0)

3. 插入节点

模仿内核链表的 list_addlist_add_tail 函数,来实现节点插入操作。将节点插入到链表尾部的函数。

// 在链表末尾插入节点
void list_add_tail(struct my_list_head *new, struct my_list_head *head) {
    struct my_list_head *last = head->prev;
    new->next = head;
    new->prev = last;
    last->next = new;
    head->prev = new;
}

4. 删除节点

删除节点时需要更新前驱节点和后继节点的指针,确保链表的结构正确。

// 删除节点
void list_del(struct my_list_head *entry) {
    struct my_list_head *prev = entry->prev;
    struct my_list_head *next = entry->next;
    prev->next = next;
    next->prev = prev;
}

5. 遍历链表

我们可以实现一个遍历链表的函数,遍历所有的节点并打印其中的数据。

// 遍历链表并打印数据
void print_list(struct my_list_head *head) {
    struct my_list_head *pos;
    struct my_node *node;

    for (pos = head->next; pos != head; pos = pos->next) {
        node = (struct my_node *)((char *)pos - offsetof(struct my_node, list));
        printf("Node data: %d\n", node->data);
    }
}

6. 释放节点

遍历链表时可以删除并释放节点的内存。这里提供一个清空链表的函数。

// 清空链表并释放内存
void clear_list(struct my_list_head *head) {
    struct my_list_head *pos, *next;
    struct my_node *node;

    for (pos = head->next; pos != head; pos = next) {
        next = pos->next;
        node = (struct my_node *)((char *)pos - offsetof(struct my_node, list));
        list_del(pos);
        free(node);
    }
}

可以在 main 函数中测试链表的插入、遍历、删除等操作。

int main() {
    // 初始化链表头
    struct my_list_head my_list;
    INIT_LIST_HEAD(&my_list);

    // 添加节点
    for (int i = 1; i <= 3; i++) {
        struct my_node *new_node = (struct my_node *)malloc(sizeof(struct my_node));
        new_node->data = i;
        list_add_tail(&new_node->list, &my_list);
    }

    // 打印链表
    printf("List contents:\n");
    print_list(&my_list);

    // 清空链表
    clear_list(&my_list);

    return 0;
}
  • 链表节点结构:我们定义了 my_list_head 来表示链表节点的指针部分,类似于内核中的 list_head。同时,my_node 包含了数据和嵌入的链表指针。
  • 初始化链表:通过 INIT_LIST_HEAD 宏,将链表初始化为一个空的循环双向链表。
  • 插入节点list_add_tail 函数将新节点插入到链表的末尾。新节点会连接到链表的最后一个节点,并更新链表头的 prev 指针。
  • 删除节点list_del 函数会将指定节点从链表中移除,并更新前驱节点和后继节点的指针。
  • 遍历链表:通过 print_list 函数,我们可以遍历链表中的每个节点,并打印它们的数据。
  • 释放节点clear_list 函数遍历整个链表,删除所有节点,并释放它们的内存。

实际操作

假设我们要处理一个"学生信息"的数据类型,每个学生包括姓名、年龄、成绩等信息。我们可以使用链表来存储这些学生的信息。

1. 定义复杂数据结构

扩展 my_node 结构体,包含更复杂的数据(如姓名、年龄和成绩)。

 

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

// 链表节点结构
struct my_list_head {
    struct my_list_head *prev;
    struct my_list_head *next;
};

// 复杂数据类型——学生信息
struct student {
    char name[50];
    int age;
    float grade;
    struct my_list_head list;  // 嵌入链表结构
};

2. 初始化链表

我们可以使用宏 INIT_LIST_HEAD 来初始化链表头。

#define INIT_LIST_HEAD(ptr) do { \
    (ptr)->next = (ptr); \
    (ptr)->prev = (ptr); \
} while (0)

 

3. 插入节点

接下来,定义一个函数来插入新的学生节点到链表中。该函数将学生的信息存储在节点中,并将节点插入到链表尾部。

// 在链表末尾插入节点
void list_add_tail(struct my_list_head *new, struct my_list_head *head) {
    struct my_list_head *last = head->prev;
    new->next = head;
    new->prev = last;
    last->next = new;
    head->prev = new;
}

// 插入新的学生节点
void add_student(struct my_list_head *head, const char *name, int age, float grade) {
    struct student *new_student = (struct student *)malloc(sizeof(struct student));
    strcpy(new_student->name, name);
    new_student->age = age;
    new_student->grade = grade;
    list_add_tail(&new_student->list, head);
}

4. 遍历链表

遍历链表,打印每个学生的信息:

// 遍历链表并打印每个学生的信息
void print_students(struct my_list_head *head) {
    struct my_list_head *pos;
    struct student *stu;

    for (pos = head->next; pos != head; pos = pos->next) {
        stu = (struct student *)((char *)pos - offsetof(struct student, list));
        printf("Name: %s, Age: %d, Grade: %.2f\n", stu->name, stu->age, stu->grade);
    }
}

5. 删除节点

实现一个函数来从链表中删除指定学生的信息:

// 删除节点并释放内存
void delete_student(struct student *stu) {
    stu->list.prev->next = stu->list.next;
    stu->list.next->prev = stu->list.prev;
    free(stu);
}

// 按名字删除学生节点
void remove_student(struct my_list_head *head, const char *name) {
    struct my_list_head *pos, *next;
    struct student *stu;

    for (pos = head->next; pos != head; pos = next) {
        next = pos->next;
        stu = (struct student *)((char *)pos - offsetof(struct student, list));
        if (strcmp(stu->name, name) == 0) {
            printf("Removing student: %s\n", stu->name);
            delete_student(stu);
            return;
        }
    }
    printf("Student not found: %s\n", name);
}

6. 清空链表

清空链表时,遍历所有节点并删除:

// 清空链表并释放所有节点内存
void clear_students(struct my_list_head *head) {
    struct my_list_head *pos, *next;
    struct student *stu;

    for (pos = head->next; pos != head; pos = next) {
        next = pos->next;
        stu = (struct student *)((char *)pos - offsetof(struct student, list));
        delete_student(stu);
    }
}

7. 测试数据的链表操作

int main() {
    // 初始化链表头
    struct my_list_head student_list;
    INIT_LIST_HEAD(&student_list);

    // 插入学生信息
    add_student(&student_list, "Alice", 20, 88.5);
    add_student(&student_list, "Bob", 21, 92.3);
    add_student(&student_list, "Charlie", 19, 75.4);

    // 打印所有学生的信息
    printf("All students:\n");
    print_students(&student_list);

    // 删除一个学生
    remove_student(&student_list, "Bob");

    // 打印剩下的学生信息
    printf("\nAfter removing Bob:\n");
    print_students(&student_list);

    // 清空链表
    clear_students(&student_list);

    return 0;
}

 

8. 输出结果

运行上述程序后,输出如下:

All students:
Name: Alice, Age: 20, Grade: 88.50
Name: Bob, Age: 21, Grade: 92.30
Name: Charlie, Age: 19, Grade: 75.40

Removing student: Bob

After removing Bob:
Name: Alice, Age: 20, Grade: 88.50
Name: Charlie, Age: 19, Grade: 75.40

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值