头文件
#ifndef __studentSystem_h__ #define __studentSystem_h__ #include<stdio.h> #include<conio.h> #include<stdlib.h> #include<string.h> //定义学生信息的结构体 typedef struct _student { char name[20]; //姓名 int age; // 年龄 int ID; //学号 int score; //分数 } Student; //定义指向学生数据节点的指针结构体 typedef struct _node { Student stu; //结构体中的结构体 struct _node* pnext; // 指向下一个结构体的指针 } Node; //定义头指针 Node* phead = NULL; void welcome(void); void inputStudent(void); void printStudent(void); void saveStudent(void); void readStudent(void); void countStudent(void); Node* findStudent(void); void modifyStudent(void); void deleteStudent(void); #endif
代码部分
#include"studentSystem.h" int main(void) { //做一个死循环 while (1) { //调用了welcome函数 welcome(); //这里getch,只要输入一个字符就可以,并不需要按回车 char ch = _getch(); //根据不同的数字,来选则不同的程序 switch (ch) { //录入学生信息 case '1': inputStudent(); break; //打印学生信息 case '2': printStudent(); break; //保存学生信息 case '3': saveStudent(); break; //读取学生信息 case '4': readStudent(); break; //统计所有学生人数 case '5': countStudent(); break; //查找学生人数 case '6': { //函数的返回值是一个指针 Node* p = findStudent(); if (p == NULL) { printf("没有找到! "); } else { system("cls"); // 清屏 //如果返回值不为空,那么打印节点的信息 printf("学号: %d 姓名: %s 年龄: %d 成绩: %d ", p->stu.ID, p->stu.name, p->stu.age, p->stu.score); } system("pause"); //暂停 system("cls"); // 清屏 break; } //修改学生人数 case '7': modifyStudent(); break; //删除学生信息 case '8': deleteStudent(); break; //退出系统 case '9': { printf("感谢适用本系统 "); system("pause"); //暂停 system("cls"); // 清屏 //这里就直接返回到了main函数 return 0; } default: //如果用户输入的是乱七八糟的字符 { printf("请重新输入 "); system("pause"); //暂停 system("cls"); // 清屏 break; } } } return 0; } void welcome() { printf("************************************ "); printf(" 欢迎使用学生管理系统 "); printf("************************************* "); printf(" 请选择功能列表 "); printf("*************************************** "); printf("* 1、录入学生信息 * "); printf("* 2、打印学生信息 * "); printf("* 3、保存学生信息 * "); printf("* 4、读取学生信息 * "); printf("* 5、统计所有学生人数 * "); printf("* 6、查找学生信息 * "); printf("* 7、修改学生信息 * "); printf("* 8、删除学生信息 * "); printf("* 9、退出系统 * "); printf("*************************************** "); } void inputStudent() { //创建一个新节点 Node* pnewNode = (Node*)malloc(sizeof(Node)); pnewNode->pnext = NULL; //将节点内部的指针设置为空 //输入基本的信息,注意这里的scanf 是scanf_s,这是最新的表示方法。 printf("请输入姓名: "); scanf_s("%s", pnewNode->stu.name, 20); //输入姓名 printf("请输入学生的年龄: "); scanf_s("%d", &pnewNode->stu.age); //输入年龄,注意这里要输入& printf("请输入学生的学号: "); scanf_s("%d", &pnewNode->stu.ID); //输入学生的序号 printf("请输入学生的成绩: "); scanf_s("%d", &pnewNode->stu.score); // 输入学生的成绩 printf("学生信息录入成功 "); system("pause"); //暂停 system("cls"); // 清屏 //将头文件中的做为全局变量的头节点,连接到这个新创建的节点 if (phead == NULL) { //将新节点连接到头结点 phead = pnewNode; } else { //注意: 这里是不断的在头结点往前插入新的节点,同以前的方法不一样 pnewNode->pnext = phead; //将新节点插在头结点的前面 phead = pnewNode; //移动头结点到最前面 } } //打印学生信息 void printStudent() { //首先清楚前面的关于表格的基本信息 system("cls"); //打印头部的基本信息 printf("*************************************** "); printf("* 学号 * 姓名 * 年龄 * 成绩 * "); printf("*************************************** "); //遍历链表 //首先定义一个新的指针,这个指针同头指针是一样的。 Node* p = phead; //通过循环遍历链表 while (p != NULL) { printf("* %d * %s * %d * %d * ", p->stu.ID, p->stu.name, p->stu.age, p->stu.score); //将p指针往后挪一个位置,一直到最后一个指针位置,因为最后一个节点的包含的指针为空。 p = p->pnext; } system("pause"); //暂停 system("cls"); // 清屏 } //保存学生信息 void saveStudent() { //打开文件,注意当使用fopen_s时这么写 FILE* fp; //定义了一个返回的int类型,当文件打开正确时,返回1,否则返回0 int nu; //注意这里的fopen_s 里面的指正前面带有取地址符号,而在fopen函数中,则没有取地址符号 nu = fopen_s(&fp, "C:\users\mike1\desktop\studentInformation.dat", "w"); //在c语言中0代表成功,非0代表错误。 if (nu == 1) { printf("打开文件失败! "); } //创建一个与头结点一样的节点 Node* pp = phead; //一直从头结点开始遍历,直到遍历完整个列表 while (pp != NULL) { fwrite(&pp->stu, 1, sizeof(Student), fp); //向下移动头结点 pp = pp->pnext; } //关闭文件 fclose(fp); printf("保存文件成功! "); system("pause"); //暂停 system("cls"); // 清屏 } void readStudent() { //首先定义一个文件指针 FILE* fp; int er; //这是打开文件的新的写法,er 返回0或者1 //但是实际情况是,当文件打不开时,根本不会进行到if语句。 er = fopen_s(&fp, "C:\users\mike1\desktop\studentInformation.dat", "rb"); if (er == 1) { printf("打开文件失败! "); return; } //读取文件,如果打开的没有到最后. //这是判断文件是否是空文件的另一种方式,先判断能不能正确读取所需要的字节,如果不可以,则不执行此循环 //首先定义了一个节点 Student stu1; //然后将内容先存放在前面的节点中,因为新节点是后定义的,所以如果直接将内容放在新节点,会出现,没有定义。 while (fread(&stu1, 1, sizeof(Student), fp)) //里面的函数会返回读取的字节数 { // int c; //c = fgetc(fp); ////注意此时,当feof返回1时,表示错误,即文件是空文件,这和while的逻辑正好相反。 //if (feof(fp)) //{ // break; //} //创建一个新的节点,并且将读到的数据保存在新节点中,并由此创建一个链表 Node* pp = (Node*)malloc(sizeof(Node)); //将stu里面的内容复制给新创造的节点,后面表示复制的字节数 memcpy(&pp->stu, &stu1, sizeof(Student)); //将新节点的指针设置为空 pp->pnext = NULL; //将头文件中的做为全局变量的头节点,连接到这个新创建的节点,这是在构建一个链表 if (phead == NULL) { //将新节点连接到头结点 phead = pp; } else { //注意: 这里是不断的在头结点往前插入新的节点,同以前的方法不一样 pp->pnext = phead; //将新节点插在头结点的前面 phead = pp; //移动头结点到最前面 } } fclose(fp); printf("数据加载成功! "); system("pause"); //暂停 system("cls"); // 清屏 } //统计所有学生人数 void countStudent() { //这里用的是全局变量的头结点 //初始化学生总数 int count = 0; //一个与头结点一样的节点 Node* pp = phead; //不断遍历节点 while (pp != NULL) { count++; pp = pp->pnext; } //打印学生总人数 printf("学生总人数为:%d ", count); system("pause"); //暂停 system("cls"); // 清屏 } //查找学生信息 Node* findStudent() { //定义基本变量 char stuName[10]; int stuNum; printf("请输入要查找的学生姓名: "); //在输入字节时,用最细的scanf表示方法 scanf_s("%s", stuName, 10); printf("请输入要查找到的学生的学号: "); scanf_s("%d", &stuNum); //定义一个与头结点相同的指针 Node* p = phead; //循环遍历节点 while (p != NULL) { //strcmp 表示比较两个字符串,如果相同则返回0, 不相同则返回1 if (stuNum == p->stu.age || 0 == strcmp(p->stu.name, stuName)) { //返回这个节点 return p; } p = p->pnext; } //此时p为空节点 return p; } //修改学生信息 void modifyStudent() { //定义基本的变量 int nu; printf("请输入要修改的学生的学号: "); scanf_s("%d", &nu); //定义一个与头结点相同的指针 Node* p = phead; //循环遍历节点 while (p != NULL) { //如果找到了这个节点 if (nu == p->stu.ID) { printf("请输入要修改的 姓名 年龄 成绩 "); //实验证明可以这么连着写 scanf_s("%s%d%d", p->stu.name, 20, &p->stu.age, &p->stu.score); printf("修改成功 "); break; } p = p->pnext; } if (p == NULL) { printf("没有找到此节点 "); } system("pause"); //暂停 system("cls"); // 清屏 } //删除头结点 void deleteStudent() { //定义要删除的学号 int num; printf("请输入要删除的学生的学号: "); scanf_s("%d", &num); //如果要删除的节点是头结点 //定义一个用于删除节点的指针 Node* pd; if (phead->stu.ID == num) { pd = phead; //将头结点向后移动一个节点 phead = phead->pnext; //删除头结点 free(pd); //产生结果 printf("删除成功 "); system("pause"); //暂停 system("cls"); // 清屏 return; } //如果要删除的节点不是头结点 //重新定义一个节点,用于删除指定的节点,并且这里重复利用了上面定义的节点 Node* pd1; //让pd去寻找需要的节点,这里头节点并不动 pd = phead; //注意这个循环同样适用于两个节点的链表 while (pd->pnext != NULL) { //如果找到了这个节点 if (pd->pnext->stu.ID == num) { //这里利用到了pd1 pd1 = pd->pnext; //将前后两个节点相连 pd->pnext = pd->pnext->pnext; //删除目标节点 free(pd1); //返回信息 printf("节点删除成功 "); system("pause"); //暂停 system("cls"); // 清屏 return; } //将节点向后移动一个位置用于寻找 pd = pd->pnext; } //如果没有找到目标节点 if (pd->pnext == NULL) { //返回信息 printf("没有找到要删除的节点 "); return; } }