1.学习嵌入式C语言毕业级项目------学生管理系统

初学者能够做出这个项目,想必计算机二级C语言也不在话下,我这个做出来供大家参考学习。

不至于说是毕业,但是能做出来也是说明你的水平很高了。

主要是对链表的运用以及数据的处理,内容包括对链表的增删减改,数据处理,文件对结构体的读写,用来当做练习C语言的项目是个很好的选择。

视频效果

视频最后读取后,不是删除后的结果的原因是:最后一次关闭之前没有点击保存。

易知的学生管理系统

下面是对项目的详细解释,最后末尾附上整个代码,直接复制粘贴到编译器,编译完直接启动就好。

1.头文件

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

标准库不用说,主要是输入和输出函数

内存分配函数要用到malloc,所以要使用stdlib.h

对于姓名这种字符串的处理,用string.h库会方便很多

系统函数cls清屏还有Sleep()延时等,都要用到系统函数,如果是vs,可能应该是sleep(),小写的

2.宏定义

typedef struct student {
	char name[10];//姓名
	int grade;//分数
	struct student *next;
} STD;
STD *h, *q, *p, *t, *k; //把要用的指针定义好
FILE *fp;//创建文件指针
//后面通用的指针

对链表进行宏定义,指针也是。我知道规范写法不是这样,但是我这样写可以不用在每个函数都要再定义一遍,方便一些。

然后我也不想整那么麻烦,就字符串和整型的数据就够了,毕竟换别的大差不差。

这里的话就是可以让STD代表struct student,后续使用就方便很多写,比如每次申请内存时,malloc前面类型和后面的大小,都方便,这里听不懂后面会知道。

然后 我默认h是头指针,k是尾指针,p,q是遍历指针,t是申请内存的指针。

最后是文件指针,主要用来打开和关闭文件的。

3.菜单目录

void sign() {
	printf("***欢迎来到学生管理系统***\n");
	printf("***输入下面数字对应功能***\n");
	printf("1.添加学生信息\n");
	printf("2.删除学生信息\n");
	printf("3.修改学生信息\n");
	printf("4.成绩按大到小排序\n");
	printf("5.成绩按小到大排序\n");
	printf("6.查询某个同学的成绩\n");
	printf("7.查询成绩平均分,总分,最高分,最低分\n");
	printf("8.查看全部成绩\n");
	printf("9.保存成绩\n");
	printf("10.读取成绩\n");
	printf("11.退出系统\n");
}

这个没有什么,就是显示菜单的相关选项内容而已

4.文件保存

void save() {
	system("cls");
	if (h == NULL) {
		printf("当前暂无学生名单!\n");
		Sleep(1000);
		return ;
	}
	fp = fopen("students.txt", "wb");//打开文件指针,创建名为students的.txt文件,wb代表二进制写入
	p = h;

	while (p != NULL) {
		fwrite(p, sizeof(STD), 1, fp);//不断把链表写入
//	p是链表,每次写入一个链表的大小,每次写入一次,写入的文件指针
		p = p->next;
	}
	fclose(fp); //读完了要关掉
	printf("保存成功");
	Sleep(1000);
	return ;
}

结构体一般是用fwrite来写入,我在注释里说得很清楚了,这里不过多解释。

逻辑是:先看看头节点是不是为空,有没有东西,如果没有,那么就退出。如果有,就打开文件指针fp打开这个.txt文件,wb为二进制读写的方式,因为其他方式容易出现整型数据错乱。后面就不断遍历,不断把结构体写入,最后关掉文件。

5.文件读取

void load() {
	fp = fopen("students.txt", "rb");//打开文件指针,读取名为students的.txt文件,r代表二进制读取
	if (fp == NULL) {
		printf("读取失败!");
		Sleep(1000);
		return ;
	}
	while (1) {
		t = (STD *)malloc(sizeof(STD)); // 为新节点分配内存
		if (fread(t, sizeof(STD), 1, fp) != 1) { // 尝试读取数据
			free(t); // 如果读取失败,释放内存
			break; // 退出循环
		}
		if (h == NULL) {
			h = t;
			k = t; // 初始化头指针和尾指针
		} else {
			k->next = t; // 将新节点添加到链表末尾
			k = t; // 更新尾指针
		}
		printf("%s %d\n", t->name, t->grade); // 打印读取的数据观察有没有问题
	}
	fclose(fp); //读完了要关掉
	printf("读取成功");
	Sleep(1000);
	return ;
}

这个读取,注释里面已经讲得很清楚了,我就不多说,就主要说一下fread()函数。

这个函数读取完后返回的是数据量,读取结构体的情况下,一般返回1就代表结束了。

6.添加学生信息

void add() {
	system("cls");
	char sname[10];
	int sgrade;//创建数据缓冲区
	t = (STD *)malloc(sizeof(STD)); //创建一个链表大小的空间,并让t指向它
	printf("***添加学生成绩***\n");
	printf("清输入学生姓名:\n");
	scanf("%s", sname);
	sgrade = 101;
	printf("清输入学生成绩:\n");
	while (sgrade > 100 || sgrade < 0) {
		scanf("%d", &sgrade);
		if (sgrade > 100 || sgrade < 0)
			printf("输入成绩有误,请重新输入!\n");
	}
	strcpy(t->name, sname);
	t->grade = sgrade;
	t->next = NULL;
	if (h == NULL) {
		h = t;
		k = t; //如果没有创建,那么当前指针和尾指针都指向它

	} else {
		k->next = t; //生成的s添加到尾指针的下一个地址
		k = t; //尾指针指向它
	}
	printf("添加成功!");
	Sleep(1000);
	return ;
}

这里相当于添加链表,注释也详细,这里就不说了。这里我不直接给结果体上的成员赋值,是因为我发现这样程序卡死,虽然我也不懂为什么。malloc是申请内存函数,前面的(STD*)是强转类型的意思,后面的sizeof(STD)求出一个STD结构体的大小,然后通过malloc函数申请出来。

7.删除学生信息

void dele() {
	system("cls");
	char sname[10];
	if (h == NULL) {
		printf("当前暂无学生名单!\n");
		Sleep(1000);
		return ;
	}
	printf("***删除学生成绩***\n");
	printf("***当前名单***\n");
	p = h;
	while (p != NULL) {
		printf("姓名:%s \t成绩:%3d\n", p->name, p->grade);
		p = p->next;
	}
	printf("请输入要删除学生的名字\n");
	scanf("%s", sname);
	q = h;
	if (strcmp(h->name, sname) == 0) {//查询直到名字匹配
		h = h->next;
		free(q);//如果是头结点,那么直接让头指向下一个,然后释放头结点
		printf("删除成功!\n");
		Sleep(1000);
		return ;
	} else {
		//当不是头结点的时候
		while (strcmp(q->name, sname) != 0) {
			p = q;
			q = q->next;
			if (q == NULL) {
				printf("输入有误,没有找到该学生的名字!\n");
				Sleep(1000);
				return ;
			}
			if (strcmp(q->name, sname) == 0) {
				p->next = q->next; //直接跳过链接起来
				if (q == k)
					k = p->next; //如果是尾节点,那么需要指向上一个
				free(q);
				printf("删除成功!\n");
				Sleep(1000);
				return ;
			}
		}
	}
}

这里注释也很清晰,不多说,删除链表麻烦些。逻辑是先判断要删除的是不是头结点,是的话就直接让头移动到下一个。如果是中间的那么就把要删除的上一个和下一个链接起来,释放掉中间的。同时观察是不是尾节点。

8.修改和查询学生信息

void change() {
	system("cls");
	if (h == NULL) {
		printf("当前暂无学生名单!\n");
		Sleep(1000);
		return ;
	}
	printf("***修改学生成绩***\n");
	printf("***当前名单***\n");
	p = h;
	while (p != NULL) {
		printf("姓名:%s \t成绩:%3d\n", p->name, p->grade);
		p = p->next;
	}
	printf("请输入要修成绩改学生的名字\n");
	char sname[10];
	scanf("%s", sname);
	p = h;
	while (p != NULL) {
		if (strcmp(p->name, sname) == 0) {
			printf("查找成功!\n");
			printf("请输入要修成绩的数值\n");
			scanf("%d", &p->grade);
			printf("修改成功!");
			Sleep(1000);
			return ;
		}
		p = p->next;
	}
}

void check() {
	p = h;
	system("cls");
	if (h == NULL) {
		printf("当前暂无学生名单!\n");
		Sleep(1000);
		return ;
	}
	printf("请输入要查询成绩的学生名字\n");
	char sname[10];
	scanf("%s", sname);
	while (p != NULL) {
		if (strcmp(p->name, sname) == 0) {
			printf("查询成功!\n");
			Sleep(1000);
			printf("该学生\t姓名:%s\t成绩:%d\n", p->name, p->grade);
			getchar();
			getchar();
			return ;
		}
		p = p->next;
		if (p == NULL) {

			printf("输入名字有误,请重新查询!\n");
			Sleep(1000);
			return ;
		}
	}
}

这里的话就没有加分数输入是否正确的检测,懂得查改这个功能就好了

9.大到小和小到大程序排序

void large() {
	p = h;
	q = h;
	system("cls");
	if (h == NULL) {
		printf("当前暂无学生名单!\n");
		Sleep(1000);
		return ;
	}
	int sgrade, num = 0;
	char sname[10];
	p = h;
	while (p != NULL) {
		num++;
		p = p->next;
	}
	while (num--) {
		p = h;
		q = p->next;
		while (q != NULL) {
			if (p->grade < q->grade) {
				sgrade = p->grade;
				strcpy(sname, p->name);

				p->grade = q->grade;
				strcpy(p->name, q->name);

				q->grade = sgrade;
				strcpy(q->name, sname);
			}
			p = p->next;
			q = p->next;
		}
	}
	printf("排序完成!");
	Sleep(1000);
	return ;
}

void miner() {
	p = h;
	q = h;
	int sgrade, num = 0;
	char sname[10];
	system("cls");
	if (h == NULL) {
		printf("当前暂无学生名单!\n");
		Sleep(1000);
		return ;
	}
	while (p != NULL) {
		num++;
		p = p->next;
	}
	while (num--) {
		p = h;
		q = p->next;
		while (q != NULL) {
			if (p->grade > q->grade) {
				sgrade = p->grade;
				strcpy(sname, p->name);

				p->grade = q->grade;
				strcpy(p->name, q->name);

				q->grade = sgrade;
				strcpy(q->name, sname);
			}
			p = p->next;
			q = p->next;
		}
	}
	printf("排序完成!");
	Sleep(1000);
	return ;
}

这个是基本排序的方法,冒泡排序法,没什么好说的,只是对结构体排序,也要考虑*next,不能简单直接交换节点,容易导致信息错乱,然后字符串要用strcoy,不能简单赋值。

10.统计学生信息

void analysis() {
	system("cls");
	double ave;
	int sum = 0, max = h->grade, min = h->grade, num = 0;
	p = h;
	while (p != NULL) {
		num++;

		sum += p->grade;

		if (p->grade > max)
			max = p->grade;

		if (p->grade < min)
			min = p->grade;

		p = p->next;
	}

	ave = sum / (double)num;

	p = h;
	while (p != NULL) {
		printf("姓名:%s \t成绩:%3d\n", p->name, p->grade);
		p = p->next;
	}


	printf("\n\n总分为%d\n平均分为%.2f\n总人数为%d\n最高分为%d\n最低分为%d\n", sum, ave, num, max, min);
	getchar();
	getchar();
	return ;
}

基本统计数据方法,也没有什么好说的

11.浏览学生信息

void look() {
	p = h;
	system("cls");
	if (h == NULL) {
		printf("当前暂无学生名单!\n");
		Sleep(1000);
		return ;
	}
	printf("***学生成绩***\n");
	while (p != NULL) {
		printf("姓名:%s \t成绩:%3d\n", p->name, p->grade);
		p = p->next;
	}
	getchar();
	getchar();
	return ;
}

这个是对链表进行遍历,也没什么好说的

12.菜单选表以及主函数


void menu() {
	int option;
	while (1) {
		system("cls");
		sign();
		scanf("%d", &option);
		switch (option) {
			case 1:
				add();
				break;
			case 2:
				dele();
				break;
			case 3:
				change();
				break;
			case 4:
				large();
				break;
			case 5:
				miner();
				break;
			case 6:
				check();
				break;
			case 7:
				analysis();
				break;
			case 8:
				look();
				break;
			case 9:
				save();
				break;
			case 10:
				load();
				break;
			case 11:
				exit(1);
				break;
		}
	}
}


int main() {
	h = NULL;
	menu();
	return 0;
}

主要是菜单功能的写法,主要是switch配合无限循环的用法

12.全部代码---直接复制粘贴这里就可以运行整个程序了

不需要一个个模块的复制粘贴

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

typedef struct student {
	char name[10];//姓名
	int grade;//分数
	struct student *next;
} STD;
STD *h, *q, *p, *t, *k; //把要用的指针定义好
FILE *fp;//创建文件指针
//后面通用的指针

void sign() {
	printf("***欢迎来到学生管理系统***\n");
	printf("***输入下面数字对应功能***\n");
	printf("1.添加学生信息\n");
	printf("2.删除学生信息\n");
	printf("3.修改学生信息\n");
	printf("4.成绩按大到小排序\n");
	printf("5.成绩按小到大排序\n");
	printf("6.查询某个同学的成绩\n");
	printf("7.查询成绩平均分,总分,最高分,最低分\n");
	printf("8.查看全部成绩\n");
	printf("9.保存成绩\n");
	printf("10.读取成绩\n");
	printf("11.退出系统\n");
}

void save() {
	system("cls");
	if (h == NULL) {
		printf("当前暂无学生名单!\n");
		Sleep(1000);
		return ;
	}
	fp = fopen("students.txt", "wb");//打开文件指针,创建名为students的.txt文件,wb代表二进制写入
	p = h;

	while (p != NULL) {
		fwrite(p, sizeof(STD), 1, fp);//不断把链表写入
//	p是链表,每次写入一个链表的大小,每次写入一次,写入的文件指针
		p = p->next;
	}
	fclose(fp); //读完了要关掉
	printf("保存成功");
	Sleep(1000);
	return ;
}

void load() {
	fp = fopen("students.txt", "rb");//打开文件指针,读取名为students的.txt文件,r代表二进制读取
	if (fp == NULL) {
		printf("读取失败!");
		Sleep(1000);
		return ;
	}
	while (1) {
		t = (STD *)malloc(sizeof(STD)); // 为新节点分配内存
		if (fread(t, sizeof(STD), 1, fp) != 1) { // 尝试读取数据
			free(t); // 如果读取失败,释放内存
			break; // 退出循环
		}
		if (h == NULL) {
			h = t;
			k = t; // 初始化头指针和尾指针
		} else {
			k->next = t; // 将新节点添加到链表末尾
			k = t; // 更新尾指针
		}
		printf("%s %d\n", t->name, t->grade); // 打印读取的数据观察有没有问题
	}
	fclose(fp); //读完了要关掉
	printf("读取成功");
	Sleep(1000);
	return ;
}

void add() {
	system("cls");
	char sname[10];
	int sgrade;//创建数据缓冲区
	t = (STD *)malloc(sizeof(STD)); //创建一个链表大小的空间,并让t指向它
	printf("***添加学生成绩***\n");
	printf("清输入学生姓名:\n");
	scanf("%s", sname);
	sgrade = 101;
	printf("清输入学生成绩:\n");
	while (sgrade > 100 || sgrade < 0) {
		scanf("%d", &sgrade);
		if (sgrade > 100 || sgrade < 0)
			printf("输入成绩有误,请重新输入!\n");
	}
	strcpy(t->name, sname);
	t->grade = sgrade;
	t->next = NULL;
	if (h == NULL) {
		h = t;
		k = t; //如果没有创建,那么当前指针和尾指针都指向它

	} else {
		k->next = t; //生成的s添加到尾指针的下一个地址
		k = t; //尾指针指向它
	}
	printf("添加成功!");
	Sleep(1000);
	return ;
}

void dele() {
	system("cls");
	char sname[10];
	if (h == NULL) {
		printf("当前暂无学生名单!\n");
		Sleep(1000);
		return ;
	}
	printf("***删除学生成绩***\n");
	printf("***当前名单***\n");
	p = h;
	while (p != NULL) {
		printf("姓名:%s \t成绩:%3d\n", p->name, p->grade);
		p = p->next;
	}
	printf("请输入要删除学生的名字\n");
	scanf("%s", sname);
	q = h;
	if (strcmp(h->name, sname) == 0) {//查询直到名字匹配
		h = h->next;
		free(q);//如果是头结点,那么直接让头指向下一个,然后释放头结点
		printf("删除成功!\n");
		Sleep(1000);
		return ;
	} else {
		//当不是头结点的时候
		while (strcmp(q->name, sname) != 0) {
			p = q;
			q = q->next;
			if (q == NULL) {
				printf("输入有误,没有找到该学生的名字!\n");
				Sleep(1000);
				return ;
			}
			if (strcmp(q->name, sname) == 0) {
				p->next = q->next; //直接跳过链接起来
				if (q == k)
					k = p->next; //如果是尾节点,那么需要指向上一个
				free(q);
				printf("删除成功!\n");
				Sleep(1000);
				return ;
			}
		}
	}
}

void change() {
	system("cls");
	if (h == NULL) {
		printf("当前暂无学生名单!\n");
		Sleep(1000);
		return ;
	}
	printf("***修改学生成绩***\n");
	printf("***当前名单***\n");
	p = h;
	while (p != NULL) {
		printf("姓名:%s \t成绩:%3d\n", p->name, p->grade);
		p = p->next;
	}
	printf("请输入要修成绩改学生的名字\n");
	char sname[10];
	scanf("%s", sname);
	p = h;
	while (p != NULL) {
		if (strcmp(p->name, sname) == 0) {
			printf("查找成功!\n");
			printf("请输入要修成绩的数值\n");
			scanf("%d", &p->grade);
			printf("修改成功!");
			Sleep(1000);
			return ;
		}
		p = p->next;
	}
}

void large() {
	p = h;
	q = h;
	system("cls");
	if (h == NULL) {
		printf("当前暂无学生名单!\n");
		Sleep(1000);
		return ;
	}
	int sgrade, num = 0;
	char sname[10];
	p = h;
	while (p != NULL) {
		num++;
		p = p->next;
	}
	while (num--) {
		p = h;
		q = p->next;
		while (q != NULL) {
			if (p->grade < q->grade) {
				sgrade = p->grade;
				strcpy(sname, p->name);

				p->grade = q->grade;
				strcpy(p->name, q->name);

				q->grade = sgrade;
				strcpy(q->name, sname);
			}
			p = p->next;
			q = p->next;
		}
	}
	printf("排序完成!");
	Sleep(1000);
	return ;
}

void miner() {
	p = h;
	q = h;
	int sgrade, num = 0;
	char sname[10];
	system("cls");
	if (h == NULL) {
		printf("当前暂无学生名单!\n");
		Sleep(1000);
		return ;
	}
	while (p != NULL) {
		num++;
		p = p->next;
	}
	while (num--) {
		p = h;
		q = p->next;
		while (q != NULL) {
			if (p->grade > q->grade) {
				sgrade = p->grade;
				strcpy(sname, p->name);

				p->grade = q->grade;
				strcpy(p->name, q->name);

				q->grade = sgrade;
				strcpy(q->name, sname);
			}
			p = p->next;
			q = p->next;
		}
	}
	printf("排序完成!");
	Sleep(1000);
	return ;
}

void check() {
	p = h;
	system("cls");
	if (h == NULL) {
		printf("当前暂无学生名单!\n");
		Sleep(1000);
		return ;
	}
	printf("请输入要查询成绩的学生名字\n");
	char sname[10];
	scanf("%s", sname);
	while (p != NULL) {
		if (strcmp(p->name, sname) == 0) {
			printf("查询成功!\n");
			Sleep(1000);
			printf("该学生\t姓名:%s\t成绩:%d\n", p->name, p->grade);
			getchar();
			getchar();
			return ;
		}
		p = p->next;
		if (p == NULL) {

			printf("输入名字有误,请重新查询!\n");
			Sleep(1000);
			return ;
		}
	}
}

void analysis() {
	system("cls");
	double ave;
	int sum = 0, max = h->grade, min = h->grade, num = 0;
	p = h;
	while (p != NULL) {
		num++;

		sum += p->grade;

		if (p->grade > max)
			max = p->grade;

		if (p->grade < min)
			min = p->grade;

		p = p->next;
	}

	ave = sum / (double)num;

	p = h;
	while (p != NULL) {
		printf("姓名:%s \t成绩:%3d\n", p->name, p->grade);
		p = p->next;
	}


	printf("\n\n总分为%d\n平均分为%.2f\n总人数为%d\n最高分为%d\n最低分为%d\n", sum, ave, num, max, min);
	getchar();
	getchar();
	return ;
}

void look() {
	p = h;
	system("cls");
	if (h == NULL) {
		printf("当前暂无学生名单!\n");
		Sleep(1000);
		return ;
	}
	printf("***学生成绩***\n");
	while (p != NULL) {
		printf("姓名:%s \t成绩:%3d\n", p->name, p->grade);
		p = p->next;
	}
	getchar();
	getchar();
	return ;
}

void menu() {
	int option;
	while (1) {
		system("cls");
		sign();
		scanf("%d", &option);
		switch (option) {
			case 1:
				add();
				break;
			case 2:
				dele();
				break;
			case 3:
				change();
				break;
			case 4:
				large();
				break;
			case 5:
				miner();
				break;
			case 6:
				check();
				break;
			case 7:
				analysis();
				break;
			case 8:
				look();
				break;
			case 9:
				save();
				break;
			case 10:
				load();
				break;
			case 11:
				exit(1);
				break;
		}
	}
}


int main() {
	h = NULL;
	menu();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值