基于C语言单链表的成绩管理程序

单向链表实现增删改查

1.数据结构

typedef struct item
{
	item *prev = NULL;
	item *next = NULL;
	char name[20];
	int math;
	int cna;
	int eng;
	int avg;
	bool flag = false;//有内容标志,可以忽略掉第一个数据得问题
}date;

*prev------------->指向上一个数据存储空间的指针
*next------------->是指向下一个数据存储空间的指针

name------------>姓名char数组
cna--------------->语文成绩
eng--------------->英语成绩
math------------->数学成绩
avg--------------->平均分

flag-------------->是否具有数据标志,可以避免释放表头出现的内存泄漏问题

2.链表增加项目

void additem()//增
{
	date *head;
	date *ptr;
	ptr = &first;
	while (true)
	{
		if (ptr->flag == false)//该内存区域下没有数据
		{
			printf("name:\n");
			fflush(stdin);//清除缓存空间 否则gets_s会因为检测缓存空间的空字符而跳过
			gets_s(ptr->name);
			printf("Math:\n");
			scanf_s("%d", &ptr->math);
			printf("Chinese:\n");
			scanf_s("%d", &ptr->cna);
			printf("English:\n");
			scanf_s("%d", &ptr->eng);
			ptr->avg = (ptr->cna + ptr->eng + ptr->math) / 3;
			ptr->flag = true;
			printf("write succed!\n\n\n");
			break;
		}
		else//如果没有数据
		{
			if (ptr->next == NULL)//尚无开空间,开新空间
			{
				head = ptr;//记录前一个数据存储空间
				ptr->next = (date*)malloc(sizeof(date));//开辟一个存储空间
				ptr = ptr->next;
				ptr->next = NULL;//初始化部分变量
				ptr->prev = head;//记录上一个数据
				
				ptr->flag = false;
			}
			else
			{
				ptr = ptr->next;//还有数据只想下一个数据存储空间
			}
		}
	}
}


#first为全局变量意为第一个date类型数据  定义为date first;
沿着 *next寻找空的数据,找到则分配存储空间,将有内容标志初始化为 false


3.展示数据

遍历整个链表,并将数据打印出来

void display()
{
	date *ptr;
	ptr = &first;//指向第一个存储空间
	if (ptr->flag == false)
	{
		printf("no data in the link list\n");
		return;
	}
	printf("***********************************************\n");
	printf("Name     Math     Chinese     English     AVG\n");
	while (true)
	{
		printf("%-9s%-9d%-12d%-12d%-3d\n", ptr->name, ptr->math, ptr->cna, ptr->eng, ptr->avg);
		if (ptr->next == NULL)break;
		else ptr = ptr->next;
	}
	printf("**********************************************\n");
}

初始化一个*ptr ptr等于first(第一个数据)的地址,通过*next指针遍历整个链表当*next指向空遍历结束

输入数据打印效果如下:




4.查找元素

date* search()//查
{
	date *ptr;
	ptr = &first;//指向第一个存储空间

	char name[20];
	printf("name:\n");
	fflush(stdin);
	gets_s(name);

	while (true)
	{
		if (strcmp(ptr->name, name) == 0)break;//找到结果结束遍历
		else if (ptr->next == NULL)///遍历结束未找到结果
		{
			printf("no date!\n");
			return NULL;
		}
		else ptr = ptr->next;
	}
	printf("check the information\n");
	printf("***********************************************\n");
	printf("Name     Math     Chinese     English     AVG\n");
	printf("%-9s%-9d%-12d%-12d%-3d\n", ptr->name, ptr->math, ptr->cna, ptr->eng, ptr->avg);
	printf("**********************************************\n\n");
	return ptr;
}


因为后面删,改多需要查询元素,该函数需要在后面进行调用,所以返回一个date类型指针即查找的结果。

date类型指针ptr最初指向first,之后进行遍历直到找到name与输入值相同的项,返回ptr指针(即指向匹配项地址)
查询效果如下:




5.修改链表内的元素

有了查找的基础,修改就非常容易实现了。
只需要查找获取匹配项的指针后,修改其值然后返回即可。
void modify()//改
{
	char k=0;
	date*ptr;
	ptr = search();
	printf("sure?(Y/N)\n");
	k=_getch();
	if (k == 'Y' || k == 'y')
	{
		printf("name:\n");
		fflush(stdin);
		gets_s(ptr->name);
		printf("Math:\n");
		scanf_s("%d",&ptr->math);
		printf("Chinese:\n");
		scanf_s("%d", &ptr->cna);
		printf("English:\n", &ptr->eng);
		scanf_s("%d", &ptr->eng);
		printf("modify complete!\n");
	}
}


6.删除链表中元素

首先还是需要调用search()函数获取匹配项指针,然后会出现多种情况:
1. *prev不为空时
prev->next=ptr->next
2.若*prev为空 则说明已经是指向第一个数据first first=ptr->next即可
void delitem()//删
{
	date *ptr;
	ptr = search();
	if (ptr == NULL)return;//没有搜寻到相关元素
	printf("Are you sure?(Y/N)\n");
	char key= _getch();
	if (key == 'Y' || key == 'y')
	{
		if (ptr->prev != NULL)
		{
			ptr->prev->next = ptr->next;
			ptr->next->prev = ptr->prev;
			free(ptr);
		}
		else//如果是表头元素将其重新初始化即可
		{
			if (ptr->next != NULL)
			{
				first = *ptr->next;
				first.prev = NULL;
			}
			else
			{
				printf("already no data in link list\n");
				first.flag = false;
			}
		}
		
	}

}




二.内核链表 内核链表是一种链表,Linux内核中的链表都是用这种形式实现的 1.特性 内核链表是一种双向循环链表,内核链表的节点节点结构中只有指针域 使用内核链表的时候,将内核链表作为一个成员放入到一个结构体中使用 我们在链表中找到内核链表结构的地址,通过这个地址就可以找到外部大结构体的地址,通过大结构体就可以访问其中的成员 优势: 内核链表突破了保存数据的限制,可以用内核链表来保存任何数据(使用一种链表表示各种类型的数据,通用性很强) 内核链表中只有指针域,维护起来更加方便,效率更高 2.使用 内核链表在内核中已经被实现,我们只需要调用其接口直接使用即可 内核链表的实现代码在内核源代码的list.h文件中 3.源代码分析 (1)节点结构: struct list_head { struct list_head *next, *prev;//前置指针 后置指针 }; (2)初始化 #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) (3)插入 //从头部插入 static inline void list_add(struct list_head *new, struct list_head *head)//传入要插入的节点和要插入的链表 { __list_add(new, head, head->next); } //从尾部插入 static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } (4)通过节点找到外部结构体的地址 //返回外部结构体的地址,第一个参数是节点地址,第二个参数是外部结构体的类型名,第三个参数是节点在外部结构体中的成员名 #define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) (5)遍历内核链表 //遍历内核链表 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) //安全遍历内核链表 #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) 二.内核链表 内核链表是一种链表,Linux内核中的链表都是用这种形式实现的 1.特性 内核链表是一种双向循环链表,内核链表的节点节点结构中只有指针域 使用内核链表的时候,将内核链表作为一个成员放入到一个结构体中使用 我们在链表中找到内核链表结构的地址,通过这个地址就可以找到外部大结构体的地址,通过大结构体就可以访问其中的成员 优势: 内核链表突破了保存数据的限制,可以用内核链表来保存任何数据(使用一种链表表示各种类型的数据,通用性很强) 内核链表中只有指针域,维护起来更加方便,效率更高 2.使用 内核链表在内核中已经被实现,我们只需要调用其接口直接使用即可 内核链表的实现代码在内核源代码的list.h文件中 3.源代码分析 (1)节点结构: struct list_head { struct list_head *next, *prev;//前置指针 后置指针 }; (2)初始化 #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) (3)插入 //从头部插入 static inline void list_add(struct list_head *new, struct list_head *head)//传入要插入的节点和要插入的链表 { __list_add(new, head, head->next); } //从尾部插入 static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } (4)通过节点找到外部结构体的地址 //返回外部结构体的地址,第一个参数是节点地址,第二个参数是外部结构体的类型名,第三个参数是节点在外部结构体中的成员名 #define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) (5)遍历内核链表 //遍历内核链表 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) //安全遍历内核链表 #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) 二.内核链表 内核链表是一种链表,Linux内核中的链表都是用这种形式实现的 1.特性 内核链表是一种双向循环链表,内核链表的节点节点结构中只有指针域 使用内核链表的时候,将内核链表作为一个成员放入到一个结构体中使用 我们在链表中找到内核链表结构的地址,通过这个地址就可以找到外部大结构体的地址,通过大结构体就可以访问其中的成员 优势: 内核链表突破了保存数据的限制,可以用内核链表来保存任何数据(使用一种链表表示各种类型的数据,通用性很强) 内核链表中只有指针域,维护起来更加方便,效率更高 2.使用 内核链表在内核中已经被实现,我们只需要调用其接口直接使用即可 内核链表的实现代码在内核源代码的list.h文件中 3.源代码分析 (1)节点结构: struct list_head { struct list_head *next, *prev;//前置指针 后置指针 }; (2)初始化 #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) (3)插入 //从头部插入 static inline void list_add(struct list_head *new, struct list_head *head)//传入要插入的节点和要插入的链表 { __list_add(new, head, head->next); } //从尾部插入 static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } (4)通过节点找到外部结构体的地址 //返回外部结构体的地址,第一个参数是节点地址,第二个参数是外部结构体的类型名,第三个参数是节点在外部结构体中的成员名 #define list_entry(ptr, type, member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) (5)遍历内核链表 //遍历内核链表 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) //安全遍历内核链表 #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) C语言下的单链表,可以增加,删除,查找,销毁节点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值