#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;
}