♻️ 资源大小801KB➡️资源下载https://download.csdn.net/download/s1t16/87430295学生信息管理系统问题定义目的训练学生的基本编程能力了解管理信息系统的开发流程熟悉 C 语言的文件和单链表的各种基本操作。本程序中涉及结构体、单链表、文件等方面的知识。通过本程序的训练使学生能对 C 语言的文件操作有一个更深刻的了解掌握利用单链表存储结构实现对学生成绩管理的原理为进一步开发出高质量的管理信息系统打下坚实的基础。任务及要求任务运用 C 语言中的相关知识独立设计一个学生信息管理系统并编程实现以下功能学生信息录入。进入系统录入学生成绩信息包括学生姓名、年龄、性别、电话等。学生信息删除。输入学生姓名可删除此学生在系统中保存的信息。学生信息查找。输入姓名查询并显示出该学生的相关信息。浏览全部已录入信息。将全部已录入的学生的姓名、年龄、性别、电话等信息有序的罗列出来。修改已录入的学生信息。输入需修改的学生姓名可单独修改此学生的个人信息。要求定义学生信息结构并采用链表结构对数据进行存储为了保证所有的数据可以长期被使用要求程序能将录入数据存储在外部数据文件中提供友好的用户界面方便用户操作提供提示信息和版本信息。程序的运行效果如图 1 所示按下数字键实现相应功能。图 1 学生信息管理系统菜单图系统简介设计思路采用模块化的程序设计方法即将较大的任务按照一定的原则分为一个个较小的任务然后分别设计各个小任务。各个模块大致功能如图 2 所示图 2 通讯录系统功能模块分析图程序设计主函数功能描述Main.cpp引用自定义的头文件 myList.h# includemyList.h一、菜单函数 Menu()用于输出设计好的菜单界面。voidmenu(){ system(color97);//设置默认的控制台前景和背景颜色 printf(************************************************************\n); printf(************【桂林信息科技学院学生信息管理系统】************\n); printf(************************************************************\n); printf(--------------------------------\n); printf(|1.录入信息 |\n); printf(--------------------------------\n); printf(|2.删除信息 |\n); printf(--------------------------------\n); printf(|3.查找信息 |\n); printf(--------------------------------\n); printf(|4.浏览信息 |\n); printf(--------------------------------\n); printf(|5.修改信息 |\n); printf(--------------------------------\n); printf(|0.退出系统 |\n); printf(--------------------------------\n); printf(***********************************************************\n); printf(----------→ 请按下相应的数字键选择功能 ←----------\n); printf(madeby 黄梓芫版本 V0.3\n); printf(***********************************************************\n); }二、创建链表链表是一种常见的基础数据结构结构体指针在这里得到了充分的利用。链表可以动态的进行存储分配也就是说链表是一个功能极为强大的数组他可以在节点中定义多种数据类型还可以根据需要随意增添删除插入节点。链表都有一个头指针一般以 head 来表示存放的是一个地址。链表中的节点分为两类头结点和一般节点头结点是没有数据域的。链表中每个节点都分为两部分一个数据域一个是指针域。说到这里你应该就明白了链表就如同车链子一样head 指向第一个元素第一个元素又指向第二个元素……直到最后一个元素该元素不再指向其它元素它称为“表尾”它的地址部分放一个“NULL”表示“空地址”链表到此结束。链表是一种物理存储结构上非连续、非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表的组成是由很多结点组成的。一个结点包含数据域和指针域数据域用来存放数据指针域负责指向其他结点起到链接的作用。创建头结点。命名为:head在创建一个结点用来保存每次插入的结点.命名为p循环创建一般结点。命名为s将创建的结点与已有的结点链接起来遍历链表输出数据。structNode*listcreateList();详见下文头文件中的 createLis 函数介绍三、功能选择 KeyDown()voidkeyDown() { - { - intchoice0; - structstudentdata; - structNode*pMoveNULL; - scanf(%d,choice); - switch(choice) - { - 0 退出系统 case0: - printf(---已退出---); - system(pause); - exit(0); - break; 1 录入信息 case1: - printf(------------【录入信息】------------\n); - //插入链表 - printf(请输入姓名年龄性别电话); - fflush(stdin);//清空缓存区 - scanf(%s%d%s%s,data.name,data.age,data.sex,data.tel); - insertNodeByHead(list,data); - break; 2 删除信息 case2: - printf(------------【删除信息】------------\n); - printf(请输入需删除的学生姓名:); - scanf(%s,data.name); - deleteAppoinNode(list,data.name); - break; 3 查找信息 case3: - printf(------------【查找信息】------------\n); - printf(请输入需查找的学生姓名:); - scanf(%s,data.name); - pMovesearchInfoByData(list,data.name); - if(pMoveNULL) - { - printf(未找到相关信息无法删除\n); - system(pause); - } - else - { - printf(姓名\t 年龄\t 性别\t 电话\n); printf(%s\t%d\t%s\t%s\n,pMove-data.name,pMove-data.age,pMove-data.sex,pMove-data.tel); } break; 4 浏览信息 case4: - printf(------------【浏览信息】------------\n); - printList(list); - break; 5 修改信息 case5: - printf(------------【修改信息】------------\n); - //修改学生信息 - printf(请输入需修改的学生姓名:); - scanf(%s,data.name); - pMovesearchInfoByData(list,data.name); - if(pMoveNULL) - { - printf(未找到相关信息无法修改\n); - system(pause); - } - else - { - printf(姓名\t 年龄\t 性别\t 电话\n); printf(%s\t%d\t%s\t%s\n,pMove-data.name,pMove-data.age,pMove-data.sex,pMove-data.tel); - - system(cls); - printf(修改姓名----1\n); - printf(修改年龄----2\n); - printf(修改性别----3\n); - printf(修改电话----4\n); - - printf(请输入要修改的信息:); - scanf(%d,choice); - - switch(choice) - { case1: - printf(请输入姓名); - scanf(%s,pMove-data.name); - break; case2: - printf(请输入年龄); - scanf(%s,pMove-data.age); - break; case3: - printf(请输入性别); - scanf(%d,pMove-data.sex); - break; case4: - printf(请输入电话); - scanf(%d,pMove-data.tel); - break; - } - printf(是否继续修改学生信息?y-1/n-0\n); - scanf(%d,choice); - if(choice0) - { - break; - } - } 修改后打印 printf(姓名\t年龄\t性别\t电话\n); printf(%s\t%d\t%s\t%s\n,pMove-data.name,pMove-data.age,pMove-data.sex,pMove-data.tel); break; default: printf(选择错误请重新输入\n); system(pause); break; } 同步操作到文件 } writeInfoToFile(list,学生信息.txt); }四、主函数 main()intmain() { - readInfoFromFile(list,学生信息.txt); - while(1) - { - menu(); - keyDown(); - system(pause); - system(cls); - } - system(pause); - return0; c } 头文件功能描述 c myList.h 1 标准 io 库用于 scanf, printf 标准输入输出 c # includestdio.h 2 用于 malloc, free 内存申请及释放 c # includestdlib.h 3 字符串链接函数 c # includestring.h 4 定义名为 student 的一个结构体类型 structstudent c { - charname[20]; - intage; - charsex[5]; - chartel[20]; c }; 5 定义了 Node 类型的结构体 structNode c { structstudentdata; structNode*next; }; structstudent;//存放数据 struct node *next; //地址域6 创建链表内存分配Malloc 函数: (void *)malloc(int size)malloc 函数的返回值是一个 void 类型的指针参数为 int 类型数据即申请分配的内存大小单位是 byte。内存分配成功之后malloc 函数返回这块内存的首地址。需要一个指针来接收这个地址但由于函数的返回值是 void *类型所以必须强制转换成你所要接收的类型也就是说内存将要用来存储什么类型的数据。同样在堆上分配的地址空间并没有赋予名字也就是说堆上的内存都是以匿名访问的形式进行。在使用 malloc 函数时应注意函数的返回值是不是一个可用的地址也就是说要判定堆上的空间是否被分配成功若失败函数返回的时 NULL。————————————————structNode*createList() { - structNode*headNode(structNode*)malloc(sizeof(structNode)); - headNode-nextNULL; - returnheadNode; }一个节点就像火车的一节车厢, data 是车厢里面的东西, next 相当于一个钩子, 用于将车厢之间的连接起来7 创建节点 cteateNodestructNode*cteateNode(structstudentdata) { - structNode*newNode(structNode*)malloc(sizeof(structNode)); - newNode-datadata; - newNode-nextNULL; - returnnewNode; c } 8 插入节点此处使用表头法插入insertNodeByHead voidinsertNodeByHead(structNode*headNode,structstudentdata) c { - structNode*newNodecteateNode(data); - newNode-nextheadNode-next; - headNode-nextnewNode; }插入节点就是用插入前节点的指针域链接上插入节点的数据域再把插入节点的指针域链接上插入后节点的数据域。根据图插入节点也就是e-next head-next; head-next e;9 删除节点 deleteAppoinNodevoiddeleteAppoinNode(structNode*headNode,char*name) { - structNode*posNodeheadNode-next; - structNode*posFrontNodeheadNode; - if(posNodeNULL) - { - printf(数据为空无法删除\n); - return; - } - while(strcmp(posNode-data.name,name)) - { - posFrontNodeposNode; - posNodeposFrontNode-next; - if(posNodeNULL) - { - printf(未找到指定位置无法删除\n); - return; - } - } - posFrontNode-nextposNode-next; - free(posNode); } { - structNode*posNodeheadNode-next; - structNode*posFrontNodeheadNode; - if(posNodeNULL) - { - printf(数据为空无法删除\n); - return; - } - while(strcmp(posNode-data.name,name)) - { - posFrontNodeposNode; - posNodeposFrontNode-next; - if(posNodeNULL) - { - printf(未找到指定位置无法删除\n); - return; - } - } - posFrontNode-nextposNode-next; - free(posNode); }删除链表的元素也就是把前节点的指针域越过要删除的节点指向下下个节点。即p-next q-next;然后放出 q 节点的空间即 free(q);遍历链表查找 searchInfoByData获取链表第 i 个数据的算法思路声明一个结点 p 指向链表第一个结点初始化 j 从 1 开始当 ji 时就遍历链表让 p 的指针向后移动不断指向下一个结点j 累加 1若到链表末尾 p 为空则说明第 i 个元素不存在否则查找成功返回结点 p 的数据structNode*searchInfoByData(structNode*headNode,char*name) { structNode*pMoveheadNode-next; if(pMoveNULL) returnNULL; while(strcmp(pMove-data.name,name)) { pMovepMove-next; } returnpMove; } { structNode*pMoveheadNode-next; if(pMoveNULL) returnNULL; while(strcmp(pMove-data.name,name)) { pMovepMove-next; } returnpMove; }链表的存储 readInfoFromFileFopen函数FILE *fp fopen(“demo.txt”, “r”);fopen() 会获取文件信息包括文件名、文件状态、当前读写位置等并将这些信息保存到一个 FILE 类型的结构体变量中然后将该变量的地址返回。“r”表示以“只读”方式打开当前目录下的 demo.txt 文件并使 fp 指向该文件这样就可以通过 fp 来操作 学生信息.txt 了。fp 通常被称为文件指针。打开文件出错时fopen() 将返回一个空指针也就是 NULL我们可以利用这一点来判断文件是否打开成功“w”以“写入/更新”方式打开文件相当于 w 和 r 叠加的效果。既可以读取也可以写入也就是随意更新文件。如果文件不存在那么创建一个新文件如果文件存在那么清空文件内容相当于删除原文件再创建一个新文件。voidreadInfoFromFile(structNode*headNode,char*fileName) c { - FILE*fp;//文件指针 - structstudentdata; - fpfopen(fileName,r); - if(fpNULL) - { - fpfopen(fileName,w); - } - //2 读文件 while(fscanf(fp,%s\t%d\t%s\t%s\n,data.name,data.age,data.sex,data.tel)!EOF) - { - insertNodeByHead(headNode,data); - } - //关闭文件 - fclose(fp); } - 链表的读取 writeInfoToFile - “w” 以“写入”方式打开文件。如果文件不存在那么创建一个新文件如果文件存在那么清空文件内容相当于删除原文件再创建一个新文件 - voidwriteInfoToFile(structNode*headNode,char*fileName) { - FILE*fp; - fpfopen(fileName,w); - if(fpNULL) - { - printf(文件写错误); - return; - } - structNode*pMoveheadNode-next; - while(pMove) - { fprintf(fp,%s\t%d\t%s\t%s\n,pMove-data.name,pMove-data.age,pMove-data.sex,pMove-data.tel); - pMovepMove-next; - } - fclose(fp); } 打印链表 printList voidprintList(structNode*headNode) { - structNode*pMoveheadNode-next; - printf(姓名\t 年龄\t 性别\t 电话\n); - while(pMove) - { printf(%s\t%d\t%s\t%s\n,pMove-data.name,pMove-data.age,pMove-data.sex,pMove-data.tel); - pMovepMove-next; - } - printf(\n); } { - FILE*fp;//文件指针 - structstudentdata; - fpfopen(fileName,r); - if(fpNULL) - { - fpfopen(fileName,w); - } - //2 读文件 while(fscanf(fp,%s\t%d\t%s\t%s\n,data.name,data.age,data.sex,data.tel)!EOF) - { - insertNodeByHead(headNode,data); - } - //关闭文件 - fclose(fp); } - 链表的读取 writeInfoToFile - “w” 以“写入”方式打开文件。如果文件不存在那么创建一个新文件如果文件存在那么清空文件内容相当于删除原文件再创建一个新文件 - voidwriteInfoToFile(structNode*headNode,char*fileName) { - FILE*fp; - fpfopen(fileName,w); - if(fpNULL) - { - printf(文件写错误); - return; - } - structNode*pMoveheadNode-next; - while(pMove) - { fprintf(fp,%s\t%d\t%s\t%s\n,pMove-data.name,pMove-data.age,pMove-data.sex,pMove-data.tel); - pMovepMove-next; - } - fclose(fp); } 打印链表 printList voidprintList(structNode*headNode) { - structNode*pMoveheadNode-next; - printf(姓名\t 年龄\t 性别\t 电话\n); - while(pMove) - { printf(%s\t%d\t%s\t%s\n,pMove-data.name,pMove-data.age,pMove-data.sex,pMove-data.tel); - pMovepMove-next; - } - printf(\n); }边遍历边输出程序运行截图录入信息界面删除信息界面查找信息界面浏览信息界面修改信息界面退出系统界面心得体会在实训过程中我遇到许多问题链表也是我对 C 语言更深层次接触的内容刚开始对链表一窍不通我觉得我在 c 语言的学习中其实存在很多的困难因为还有很有地方都不太明白是什么意思在做实训时都是通过一点一点的想法慢慢解决的参考资料中的一些函数也不能很快了解它的意思。经过几天不断的探索学习和请教老师也算是对链表有了初步的使用能力。编写过程中每一个函数我都要思考很久而且有很多语法上的错误花费了大量的时间在查找资料和修改错误上。通过这次实训也让我知道了原来 C 语言当中还有这么多的知识要把所学的理论知识运用于解决实际问题并没有想象中的那么容易使得自己更加清晰的认清自己与他人所存在的差距。我的 C 语言基础依旧十分薄弱想要真正的去掌握 C 语言仍需要漫长的学习特别是当我第一次使用链表时让我有那种“原来结构体还可以这样用”的感觉。非常感谢我的指导老师—王涛老师的认真负责、耐心解答还有其他的答疑老师也给予了我很多的改进建议我的程序才得以顺利运行。非常感谢帮助过我的所有老师参考资料谭浩强. C 程序设计第五版. 清华大学出版社 2005