趁着放假无事,开始用C语言开发一些小的项目,巩固基础知识的同时学习新的知识。 学生成绩管理系统实现的功能有:成绩录入、学生成绩查询、删除、修改、通过文件保存等。 开发
趁着放假无事,开始用C语言开发一些小的项目,巩固基础知识的同时学习新的知识。
学生成绩管理系统实现的功能有:成绩录入、学生成绩查询、删除、修改、通过文件保存等。
开发这样一个系统需要具备的知识:线性表(链表)、文件操作、排序(如果需要成绩排序)。
开发环境为VS2015;在Linux下没有conio.h的头文件,需要修改与getch()函数相关的代码。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <conio.h> /*学生信息结构体*/ typedef struct Node { char Name[10]; //学生姓名 char ID[15]; //学生学号 int Score[3]; //三科成绩(数学、英语、数据结构) float Ave_Sco; struct Node *next; }Lnode; void Display(); /*界面显示函数*/ void GetScore(Lnode *&h); /*成绩录入函数*/ void PrintScore(Lnode *h); /*成绩打印函数*/ void ModifyScore(Lnode *h); /*成绩修改函数*/ void FindInf(Lnode *h); /*查找信息*/ void Delete(Lnode *h); /*删除函数*/ void Quit(Lnode *h); /*退出函数*/ void SaveInf(Lnode *h); void LoadInf(Lnode *h); /*初始化链表*/ void InitList(Lnode *&head) { head = (Lnode *)malloc(sizeof(Lnode)); if (head == NULL) { printf("error!"); exit(1); } head->next = NULL; //使头节点指针域为空 } int main() { Lnode *ScoreList; //建立成绩链表,所有学生信息存放在此链表 int Function; char flag; int t = 0; InitList(ScoreList); LoadInf(ScoreList); while (1) { Display(); printf("请选择操作: "); scanf("%d", &Function); switch (Function) { case 1: while (1) { GetScore(ScoreList); printf("是否继续输入 (Y/N)"); scanf("%s", &flag); if (flag == 'N' || flag == 'n')break; } system("cls"); break; case 2: PrintScore(ScoreList); _getch(); system("cls"); break; case 3: ModifyScore(ScoreList); system("cls"); break; case 4: FindInf(ScoreList); _getch(); system("cls"); break; case 5: Delete(ScoreList); _getch(); system("cls"); break; case 6: Quit(ScoreList); break; default: printf("Error!!! 请重新输入:"); break; } //switch结束 } return 0; } /*系统界面显示*/ void Display() { printf("\t\t**********************************************\n"); printf("\t\t*************欢迎使用成绩管理系统*************\n"); printf("\t\t**********************************************\n"); printf("\t\t\t\t1、录入成绩\n"); printf("\t\t\t\t2、打印成绩\n"); printf("\t\t\t\t3、修改成绩\n"); printf("\t\t\t\t4、查找学生信息\n"); printf("\t\t\t\t5、删除学生信息\n"); printf("\t\t\t\t6、退出系统\n"); printf("\n\n\n\n\n\n"); } /*成绩录入*/ void GetScore(Lnode *&h) { Lnode *p, *q = h; char name[10], id[15]; int Math, English, Datastruct; p = (Lnode *)malloc(sizeof(Lnode)); //为学生信息申请节点 printf("请依次输入学生信息:\n"); printf("姓名 学号 数学 英语 数据结构\n"); scanf("%s %s %d %d %d", &name, &id, &Math, &English, &Datastruct); for (; q->next != NULL; q = q->next){;} //移动到尾节点 strcpy(p->Name, name); strcpy(p->ID, id); p->Score[0] = Math; p->Score[1] = English; p->Score[2] = Datastruct; p->Ave_Sco = ((float)((p->Score[0] + p->Score[1] + p->Score[2]) - 150)) / 30; p->next = NULL; q->next = p; q = p; } /*成绩打印*/ void PrintScore(Lnode *h) { Lnode *p = h->next; printf("%-14s%-8s%-8s%-8s%-8s%-8s\n","排名", "学号", "姓名", "数学", "英语", "数据结构", "平均绩点"); while (p != NULL) { printf("%-14s%-8s%-8d%-8d%-8d%.2f\n", p->ID, p->Name, p->Score[0], p->Score[1], p->Score[2], p->Ave_Sco); p = p->next; } } /*成绩修改*/ void ModifyScore(Lnode *h) { Lnode *p = h->next; char name[10], id[15]; int Math, English, Datastruct; printf("请输入学生姓名:"); scanf("%s", name); printf("请输入学生学号:"); scanf("%s", id); while (p) { if (strcmp(p->Name, name)==0 && strcmp(p->ID, id)==0) { printf("当前学生信息:\n"); printf("%-14s%-8s%-8s%-8s%-8s\n", "学号", "姓名", "数学", "英语", "数据结构"); printf("%-14s%-8s%-8d%-8d%-8d\n", p->ID, p->Name, p->Score[0], p->Score[1], p->Score[2]); printf("请输入更正后的数学成绩:"); scanf("%d", &Math); printf("请输入更正后的英语成绩:"); scanf("%d", &English); printf("请输入更正后的数据结构成绩:"); scanf("%d", &Datastruct); p->Score[0] = Math; p->Score[1] = English; p->Score[2] = Datastruct; break; } else { p = p->next; } }//while循环结束 } /*信息查找*/ void FindInf(Lnode *h) { Lnode *p = h->next; char name[10], id[15]; printf("请输入学生姓名:"); scanf("%s", name); printf("请输入学生学号:"); scanf("%s", id); while (p) { if (strcmp(p->Name, name) == 0 && strcmp(p->ID, id) == 0) { printf("当前学生信息:\n"); printf("%-14s%-8s%-8s%-8s%-8s\n", "学号", "姓名", "数学", "英语", "数据结构"); printf("%-14s%-8s%-8d%-8d%-8d\n", p->ID, p->Name, p->Score[0], p->Score[1], p->Score[2]); break; } else { p = p->next; } }//while循环结束 } /*删除*/ void Delete(Lnode *h) { Lnode *p = h, *q; q = p->next; char name[10], id[15]; printf("请输入学生姓名:"); scanf("%s", name); printf("请输入学生学号:"); scanf("%s", id); while (q) { if (strcmp(q->Name, name) == 0 && strcmp(q->ID, id) == 0) { p->next = q->next; free(q); //删除p节点 printf("删除成功\n"); break; } else { p = p->next; q = q->next; } }//while循环结束 } /*退出系统*/ void Quit(Lnode *h) { SaveInf(h); //退出时保存信息 exit(0); } /*打开文件*/ void LoadInf(Lnode *h) { Lnode *p = h; Lnode *q; //临时变量 用于保存从文件中读取的信息 FILE* file = fopen("./Information.dat", "rb"); if (!file) { printf("文件打开失败!"); return ; } /* 使用feof判断文件是否为结束要注意的问题: 当读取文件结束时,feof函数不会立即设置标志符为-1,而是 需要再读取一次后,才会设置。所以要先读一次。 */ q = (Lnode *)malloc(sizeof(Lnode)); fread(q, sizeof(Lnode), 1, file); while (!feof(file)) //一直读到文件末尾 { p->next = q; p = q; q = (Lnode *)malloc(sizeof(Lnode)); fread(q, sizeof(Lnode), 1, file); } //while循环结束 p->next = NULL; fclose(file); } /*保存信息到文件中*/ void SaveInf(Lnode *h) { Lnode *p = h->next; int flag; FILE* file = fopen("./Information.dat", "wb"); if (!file) { printf("文件打开失败!"); return; } while (p != NULL) { flag = fwrite(p, sizeof(Lnode), 1, file); //将p的内容写到文件中 if (flag != 1) { break; } p = p->next; } fclose(file); }
虽然是很简单的小项目,还是有很多问题。
一:链表相关
在写成绩录入和成绩打印功能时,发现始终只能保存(没加入文件保存)最后一个数据,确定链表的相关操作没有问题,仔细判断逻辑关系后,发现是每次在头节点传到GetScore()函数,为新节点申请内存后,直接将数据保存在了新申请的节点里面,没有将链表移动到尾节点,导致每次录入成绩,都会覆盖前一次输入的数据。解决办法是链表传到函数后,先移动到最后一个节点,将新申请的节点挂接在最后一个节点之后。
/*成绩录入*/ void GetScore(Lnode *&h) { Lnode *p, *q = h; char name[10], id[15]; int Math, English, Datastruct; p = (Lnode *)malloc(sizeof(Lnode)); //为学生信息申请节点 printf("请依次输入学生信息:\n"); printf("姓名 学号 数学 英语 数据结构\n"); scanf("%s %s %d %d %d", &name, &id, &Math, &English, &Datastruct); for (; q->next != NULL; q = q->next){;} //移动到尾节点 //保存数据 strcpy(p->Name, name); strcpy(p->ID, id); p->Score[0] = Math; p->Score[1] = English; p->Score[2] = Datastruct; p->Ave_Sco = ((float)((p->Score[0] + p->Score[1] + p->Score[2]) - 150)) / 30; //始终指向最后一个节点 p->next = NULL; q->next = p; q = p; }
二、文件操作
用文件保存遇到的问题主要是每次打印数据时除正常数据外,始终多一行乱码。判断方法是while(!feof(file))。排除错误时确定了两种可能性:多保存了一行;多读取了一行。经过某度feof()与EOF的关系后,确定是多读取了一行数据。
用feof()函数进行文件尾判断时,当文件已经到达尾部后,还需要在读取一次后,feof()函数才会返回-1,所以会出现多读一次的情况;解决办法时,在循环读取之前先将第一个数据读取出来,然后在正常读取。即注意多读一次的问题。
/*打开文件*/ void LoadInf(Lnode *h) { Lnode *p = h; Lnode *q; //临时变量 用于保存从文件中读取的信息 FILE* file = fopen("./Information.dat", "rb"); if (!file) { printf("文件打开失败!"); return ; } /* 使用feof判断文件是否为结束要注意的问题: 当读取文件结束时,feof函数不会立即设置标志符为-1,而是 需要再读取一次后,才会设置。所以要先读一次。 */ q = (Lnode *)malloc(sizeof(Lnode)); fread(q, sizeof(Lnode), 1, file); while (!feof(file)) //一直读到文件末尾 { p->next = q; p = q; q = (Lnode *)malloc(sizeof(Lnode)); fread(q, sizeof(Lnode), 1, file); } //while循环结束 p->next = NULL; fclose(file); }