c语言内核链表list.h的解释及应用_c笔记

#ifndef __DLIST_H
#define __DLIST_H

/*
目的是在一个大的结构体中用一个小的结构体来实现循环链表,
这样就可以用小的结构体来进行next和pre的指向操作,
大的结构体就用来存各种信息。
*/

/*
offsetof这个宏,功能是测量在某个结构体中,某个成员的起始地址距离结构体的
起始地址的大小是多少。
这个宏有两个参数,第一个是TYPE,第二个是MEMBER。TYPE就是结构体名,
MEMBER是结构体中的某个成员。首先将0转换成TYPE类型的指针,然后指向
传入的成员。再然后取这个成员的地址,最后,size_t是无符号整数类型,
也就是把这个成员的地址转换成一个值。
以此获得成员的相对于结构体起始地址的偏移量。
*/
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)

/*
定义了一个宏container_of,目的是获取大结构体的起始地址,
有三个参数ptr,type,member。
{}的目的是让两条语句成为一个语块,()的目的是防止语法错误。
typeof可以测量变量或表达式的类型。
*/
#define container_of(ptr, type, member) ({ \
    const typeof( ((type *)0)->member) *__mptr=(ptr);\
    (type *)( (char*)__mptr-offsetof(type, member));})

//定义了两个非法地址。
#define LIST_POISON1 ((void *) 0x00100100)
#define LIST_POISON2 ((void *) 0x00200)

//小结构体的声明,其中有前后指针。
struct list_head{
    struct list_head *next, *prev;
};

/*
这两句宏定义有关链表操作的功能。
宏LIST_HEAD_INIT(name)用于初始化一个链表头节点。
它创建一个struct list_head类型的结构体,
并将其next和prev成员指向该节点自身,形成一个空的循环链表。
宏LIST_HEAD(name)用于定义一个链表头节点。
它使用LIST_HEAD_INIT(name)宏初始化一个链表头节
点,并将其赋值给一个名为name的变量。通过这个宏定义,
我们可以方便地定义和初始化链表头节点。
*/
#define LIST_HEAD_INIT(name) { &(name), &(name)}
#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)

/*
INIT_LIST_HEAD这个宏,内容是一个do while循环。
循环应该只会执行一次,会把小结构体的next和prev
指向自身,以成为双向循环链表。
*/
#define INIT_LIST_HEAD(ptr) do{\
    (ptr)->next = (ptr); (ptr)->prev = (ptr);\
}while(0)

/*
定义了一个静态的内联的void型函数__list_add,其目的是插入新节点。
传入的是三个list_head类型指针,在最开始有声明。
函数体的内容是十分基操的双向循环链表新节点的插入操作。
*/
static inline void __list_add(struct list_head *new_node, struct list_head *prev, struct list_head *next)
{
    next->prev = new_node;
    new_node->next = next;
    new_node->prev = prev;
    prev->next = new_node;
}

// static inline void __align_pre_next(struct list_head *node)
// {
//     node->next->prev = node->prev;
//     node->prev->next = node->next;
// }

/*
这个list_add函数是上一个__list_add函数的一个封装,
传入参数,然后进行插入,是往head的后面插。
*/
static inline void list_add(struct list_head *new_no, struct list_head *head)
{
    __list_add(new_no, head, head->next);
}

/*
同样是插入新节点,但是是往后插,一样是用__list_add函数
改变参数位置就可以往head的前面插。
*/
static inline void list_add_tail(struct list_head *new_no, struct list_head *head)
{
    __list_add(new_no, head->prev, head);
}

/*
prev和next中间还有一个节点,默认的,
所以通过互相指向以删除中间的节点。
*/
static inline void __list_del(struct list_head *prev, struct list_head *next)
{
    next->prev = prev;
    prev->next = next;
}

/*
传入的是要删除的节点,通过__list_del函数让这个节点的前后节点
互相指向,然后再让这个节点的前后节点指向一个没有意义的地址。
vscode中会报警告,说void*类型的指针不能赋值给list_head*这个
类型,不过应该是没啥问题的。
*/
static inline void list_del(struct list_head *entry)
{
    __list_del(entry->prev, entry->next);
    entry->next = (void *) 0;
    entry->prev = (void *) 0;
}

/*
INIT_LIST_HEAD是上面定义的宏,功能是会把小结构体的next和prev
指向自身,以成为双向循环链表。
这个函数还是会删除节点,并让节点的前后指向自身。
*/
static inline void list_del_init(struct list_head *entry){
    __list_del(entry->prev, entry->next);
    INIT_LIST_HEAD(entry);
}

/*
通过__list_del把list的前后相连,
然后用list_add把list插到了head的后面。
*/
static inline void list_move(struct list_head *list, struct list_head *head)
{
    __list_del(list->prev, list->next);
    list_add(list, head);
}

/*
用list_add_tail就是把list移到head的前面。
*/
static inline void list_move_tail(struct list_head *list, struct list_head *head)
{
    __list_del(list->prev, list->next);
    list_add_tail(list, head);
}

/*
判断是否为空
*/
static inline int list_empty(struct list_head *head)
{
    return head->next == head;
}

/*
这个函数是把list插入到head的后面,
head是一个链表的头,list也是,所以说在连接时,
需要舍去list这个头,让list后面的第一个元素连接到head的后面,
然后再让list的最后一个元素与head原本的第一个元素相连。
这样就完成了两个链表的连接。
*/
static inline void __list_splice(struct list_head *list, struct list_head *head)
{
    struct list_head *first = list->next;
    struct list_head *last = list->prev;
    struct list_head *at = head->next;

    first->prev = head;
    head->next = first;

    last->next = at;
    at->prev = last;
}

/*
list不是空,就把list连接到head的后面。
*/
static inline void list_splice(struct list_head *list, struct list_head *head)
{
    if(!list_empty(list))
    {
        __list_splice(list, head);
    }
}

//在上面的基础上加一个初始化掉list。
static inline void list_splice_init(struct list_head *list, struct list_head *head)
{
    if(!list_empty(list))
    {
        __list_splice(list, head);
        INIT_LIST_HEAD(list);
    }
}

/*
与开头的宏container_of是一个逻辑,
都是通过小结构体的地址,获取大结构体的地址。
小结构体的地址值要比大结构体的地址值要大,所以小结构体的地址
减去偏移量就是大结构体的地址。
在windows上使用这个宏会报警告,可以使用上面的container_of。
*/
#define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

/*
不安全遍历指的是在遍历链表时,如果尝试对当前遍历的节点进行删除
(或修改指向的下一个节点),可能会导致遍历过程出错。
主要原因是在遍历过程中,遍历指针(pos)直接从当前节点移动到下一个节点(pos->next),
如果当前节点被删除,那么下一次迭代时,pos->next可能已经不再指向预期的
下一个节点,导致遍历跳过节点或者遍历到无效内存区域,引发错误。
这是向下遍历
*/
#define list_for_each(pos, head) for(pos=(head)->next; pos!=(head);pos=pos->next)

//这是向上遍历
#define list_for_each_prev(pos, head) for(pos=(head)->prev;pos!=(head);pos=pos->prev)

/*
安全遍历通过使用两个指针(pos和n)来避免这个问题。pos指针用于遍历
当前节点,而n(即next)指针在每次迭代之前就预先保存了pos的下一个
节点的地址。这样,即使在遍历过程中删除(或修改)了pos指向的节点,
遍历过程仍然可以安全地继续,因为n指针已经保存了下一个要遍历的节点的
位置,从而不会受到当前节点删除操作的影响。
*/
#define list_for_each_safe(pos, n, head) for(pos=(head)->next,n=pos->next;pos!=(head);pos=n,n=pos->next)

/*
以不安全的形式遍历链表的每个元素,并且获取大个结构体放到pos里面
*/
#define list_for_each_entry(pos,head,member) for(pos=list_entry((head)->next,typeof(*pos),member);\
    &pos->member!=(head);pos=list_entry(pos->member.next,typeof(*pos),member))

#endif 

一个简单的学生管理系统

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

enum politics_status{party_member, member, masses, other};

typedef struct students
{
    char name[20];
    char gender[12];
    unsigned short age;
    int grade;
    float height;
    float weight;
    unsigned long sno;
    enum politics_status pls;
    
    struct list_head list;
}students_s;

typedef struct classes
{
    int student_cur_numb;
    int student_max_numb;

    students_s *students_info;
}classes;

students_s *init_new_stu_node() {
    students_s *head = (students_s *)malloc(sizeof(students_s));
    if (head == NULL) {
        perror("Failed to create list head");
        return NULL;
    }

    INIT_LIST_HEAD(&head->list);

    return head;
}

//初始化班级
bool init_class_manage(classes **cls)
{
    int student_max_numb;
    printf("Please input student max number:\n");
    scanf("%d", &student_max_numb);

    *cls = (classes *)malloc(sizeof(classes));
    if((*cls) == NULL)
    {
        perror("Class initial failed!");
        return false;
    }

    //(*cls)->students_info = (students *)malloc(sizeof(students));

    (*cls)->students_info = init_new_stu_node();

    (*cls)->student_cur_numb = 0;
    (*cls)->student_max_numb = student_max_numb;
    //(*cls)->students_info = NULL;

    // (*cls)->students_info->next = (*cls)->students_info;
    // (*cls)->students_info->prev = (*cls)->students_info;

    return true;
}

//输出学生信息
void input_stu_info(students_s *new_student)
{
    printf("Please Input student info\n");

    printf("Please input student sno:\n");
    scanf("%lu", &new_student->sno);

    printf("Please input student name: \n");
    scanf("%s", new_student->name);

    printf("Please input student age:\n");
    scanf("%hd", &new_student->age);

    printf("Please input student gender: \n");
    scanf("%s", new_student->gender);

    printf("Please input student height: \n");
    scanf("%f", &new_student->height);

    printf("Please input student weight: \n");
    scanf("%f", &new_student->weight);

    printf("Please input student grade: \n");
    scanf("%d", &new_student->grade);

    printf("Please input student politics status: 0:party member, 1:member, 2:masses, 3: other\n");
    scanf("%d", &new_student->pls);

    printf("End!\n");
}

//前插法
void insert_student_info_to_class(classes *cls)
{  
    students_s *new_node = init_new_stu_node();
    input_stu_info(new_node);
    list_add(&new_node->list, &cls->students_info->list);

    cls->student_cur_numb += 1;
}

//输出学生信息
void show_student_info(const students_s *sinfo)
{
        printf("sno:%lu\n", sinfo->sno);
        printf("name:%s\n", sinfo->name);
        printf("age:%hu\n", sinfo->age);
        printf("gender:%s\n", sinfo->gender);
        printf("height:%5.2f\n", sinfo->height);
        printf("weight:%5.2f\n", sinfo->weight);
        printf("grade:%d\n", sinfo->grade);
        printf("politics status:%d\n", sinfo->pls);
        printf("******************\n");
}

//输出班级所有学生信息
void display_class_students(students_s *stu)
{
    struct list_head *pos;
    students_s *stu_pos;

    list_for_each(pos, &stu->list)
    {
        stu_pos = container_of(pos, students_s, list);
        //stu_pos = list_entry(pos, students_s, list);
        show_student_info(stu_pos);
    }
}

//按名字查找学生信息
bool show_specific_student_info(classes *cls)
{
    char name[20];
    printf("Please input name for search:\n");
    scanf("%s", name);

    struct list_head *pos;
    students_s *stu_pos;

    bool flag = false;
    list_for_each(pos, &(*cls).students_info->list)
    {
        stu_pos = container_of(pos, students_s, list);
        //stu_pos = list_entry(pos, students_s, list);
        if(strcmp(stu_pos->name, name) == 0)
        {
            show_student_info(stu_pos);
            flag = true;
        }
    }
    
    return flag;
}

//后插法插入学生信息
void behind_insert_specific_student_info(classes *cls)
{
    students_s *new_node = init_new_stu_node();
    input_stu_info(new_node);
    list_add_tail(&new_node->list, &cls->students_info->list);

    cls->student_cur_numb += 1;
}

//销毁班级
void destroy_class_manage(classes **cls)
{
    struct list_head *pos, *n;
    students_s *bpos;

    list_for_each_safe(pos, n, &(*cls)->students_info->list)
    {
        //bpos = list_entry(pos, students_s, list);
        bpos = container_of(pos, students_s, list);
        show_student_info(bpos);
        printf("Student infomation destory!!!\n");
        printf("******************\n");
        free(bpos);
    }

    (*cls)->student_cur_numb = 0;
    free((*cls));
}

//按名字查找学生并删除
void destroy_specific_student(classes *cls)
{
    char name[20];
    printf("Please input name for search:\n");
    scanf("%s", name);

    struct list_head *pos, *n;
    students_s *bpos;

    list_for_each_safe(pos, n, &cls->students_info->list)
    {
        //bpos = list_entry(pos, students_s, list);
        bpos = container_of(pos, students_s, list);
        if(strcmp(bpos->name, name) == 0)
        {
            printf("Student is found!\n");
            printf("Informaton is:\n");
            show_student_info(bpos);
            //__align_pre_next(bpos);
            list_del(&bpos->list);
            printf("Student infomation destory already!!!\n");
            printf("******************\n");
        }  
    }
}

//操作界面
void login_student_ui(void)
{
    printf("Welcome to Students Management System!\n");
    printf("Please input your action(input correspending number):\n");
    printf("1:Initial class!\n");
    printf("2:Insert new student.\n");
    printf("3:Show specific student infomation.\n");
    printf("4:Display class info.\n");
    printf("5:Destroy class.\n");
    printf("6:Destroy specific student.\n");
    printf("7:Behind insert new student.\n");
    printf("******************\n");
}

int cur_stu_num = 0;
int main(void)
{
    classes *cls = NULL;

    int cmd;

    while (1)
    {
        printf("The current student number is:%d\n", cur_stu_num);
        login_student_ui();
        
        scanf("%d", &cmd);

        switch (cmd)
        {
        case 1:
            init_class_manage(&cls);
            break;
        case 2:
            insert_student_info_to_class(cls);
            break;
        case 3:
            if(show_specific_student_info(cls))
            {
                show_specific_student_info(cls);
            }
            else
            {
                printf("Student not found!\n");
            }       
            break;
        case 4:
            display_class_students(cls->students_info);
            break;
        case 5:
            destroy_class_manage(&cls);
            break;
        case 6:
            destroy_specific_student(cls);
            break;
        case 7:
            behind_insert_specific_student_info(cls);
        default:
            break;
        }
        cur_stu_num = cls->student_cur_numb;
    }

    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值