目录
前言
随着C语言学习接近尾声,小项目实战经验,课程小作业和自己对知识牢固度的测试等一些需求的出现,经典的C语言————学生信息管理系统(小项目)详细解析便与大家见面了,本文主要介绍该管理系统的写法和小结。
一、题目及其要求
设计并实现一个学生信息管理系统(具体小项目的开发):
其中学生信息包括:学号,姓名,性别,5门课的成绩,总成绩和平均成绩;
需要实现的功能(可以对该系统进行简易的操作):对学生信息进行添加,修改,排序,查找,删除,输出和退出该系统;
二 、设计思路
- 对于需求可以先选用数据结构将信息存储
- 接着能细分该管理系统的各项功能,进行细分为可实现的函数
- 然后需要写好主控函数
- 最后就是各个函数的具体实现
- 流程图如下
三、使用步骤(代码实现)
1.库函数及其类型定义
代码如下:
#include<stdio.h> //定义了FILE结构类型名 ,文件的读写函数fread(),fwrite()
#include<string.h> //strcmp()(自己写的FindByNum()函数)
#include<ctype.h>
#include<stdlib.h> //该文件定义了1.system()函数(用在display函数)
//2.exit()函数(用在save函数)
#define N 10000
typedef struct student
{
char ID[15]; //学号
char name[25]; //姓名
char sex; //性别
double score[9]; //五门课程成绩
double total; //总成绩
double avg; //平均成绩
}STU;
2.功能函数的声明
代码如下(记得看注释):
//函数
void load(STU stu[],int *nptr); //1.把学生信息从文件存入到数组
void display(void); //2.显示主菜单,进行模式的选择
void add(STU stu[], int *nptr); //3.0 添加
void InputRecord(STU stu[], int i); //3.1 用于实现读取一个学生信息并存入数组中
void save(STU stu[] , int n); //3.2 用于将数组中的信息保存到文件中 (n个同学)
void edit(STU stu[] , int n); //4.0修改
int FindByNum(STU stu[] , int n , char *str);//4.1 比对学号,返回结构体数组下标(定位需要修改信息的学生)
void output(STU stu[] , int n); //4.2 输出所有学生信息
void PrintRecord(STU *sPtr); //4.21 输出指定学生信息
void sort(STU stu[] , int n); //5.0排序(以下函数可以在本函数中声明)
int ScoreAsc(STU a , STU b); //5.1按总成绩升序ascend
int ScoreDes(STU a , STU b); //5.2按总成绩降序desend
int NameAsc(STU a , STU b); //5.3按姓名升序
void find(STU stu[], int n); //6.查找
void del(STU stu[] , int *nptr);//7.删除(删除后总人数会变化,故用指针传输)
//8.输出 函数4.2
3.主控函数的代码显示:
int main()
{
int n; //总人数
STU stu[N]; //结构数组
int select; //记录模式选项
load(stu,&n); //有何用1.把学生信息从文件存入到数组
//当关闭后再次编译,运行。就用上了,输出时要用数组
while(1)
{
display(); //2.显示主菜单,进行选择
scanf("%d",&select);
switch(select) //模式选择
{
case 1:
add(stu,&n); break; //3.添加
case 2:
edit(stu,n); break; //4.修改
case 3:
sort(stu,n); break; //5.排序
case 4:
find(stu,n); break; //6.查找
case 5:
del(stu,&n); break; //7.删除
case 6:
output(stu,n); break; //8.输出
case 7:
return 0; //退出
}
}
}
4.功能函数的写法及代码演示:
/*1.把学生信息从文件存入到数组 */
可直接用 fread 函数 实现将文件中的信息转移到数组中
也会用到文件的打开和关闭 fopen, fclose函数
fopen函数 , 文件打开成功返回指针;文件打开失败则返回NULLfclose函数, 文件关闭成功返回0;文件关闭失败返回非零值
数据块读写fread函数, 函数调用成功 返回实际读写的次数; 调用失败返回0
void load(STU stu[], int * nptr) //nptr 用于记录学生数量
{
FILE *fp;
int i;
if((fp = fopen("D:\\student1.txt","r"))==NULL)
{
*nptr=0; //nptr 用于记录学生数量
return;
}
for(i=0;fread(&stu[i],sizeof(STU),1,fp)!=0;i++)//以是否读取到信息为判断条件
*nptr=i+1; //nptr 用于记录学生数量
/* 请观察这两行代码,小提示 “;”
for(i=0;fread(&stu[i],sizeof(STU),1,fp)!=0;i++);
*nptr=i;
*/
fclose(fp);
}
/*2.显示主菜单,进行模式的选择*/
首先知道 system()函数的用法
实现清屏操作 "CLS"
实现变换背景,前景颜色设置 "color XY"
X第一个十六进制数为背景色设置,Y第二个十六进制数为前景色设置。
system()函数的参数及实现的功能
可以参考一下:https://blog.csdn.net/dark_cy/article/details/89715625
可以参考一下:https://blog.csdn.net/luyaozhima/article/details/108069956
system(“pause”)可以实现冻结屏幕,便于观察程序的执行结果;
system(“CLS”)可以实现清屏操作。
system(color xx)函数可以改变控制台的前景色和背景,“color xx”中的第一个十六进制数为背景色设置,第二个十六进制数为前景色设置。
注:各颜色对应的数值
0 = 黑色 8 = 灰色
1 = 蓝色 9 = 淡蓝色
2 = 绿色 A = 淡绿色
3 = 湖蓝色 B = 淡浅绿色
4 = 红色 C = 淡红色
5 = 紫色 D = 淡紫色
6 = 黄色 E = 淡黄色
7 = 白色 F = 亮白色
void display(void)
{
system("CLS");
system("color 03"); //将前景颜色设置为湖蓝色,背景颜色没有改变
printf("欢迎使用本学生信息管理系统\n");
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(" |____________________________|\n");
}
/* 3. 添加 */
本函数需要实现:
连续添加 任意新记录(放在数组中),添加新记录后把信息保存到文件中
3.1 函数 InputRecord(); 用于实现读取学生信息并存入数组中
3.2 函数 save(); 用于将数组中的信息保存到文件中
/*3.0添加*/
void add(STU stu[], int *nptr)//此时 nptr 仍是记录学生人数
{
int i=0;
char sel ='y';
while(sel == 'y')
{
InputRecord(stu,(*nptr)++); //添加第n+1个学生的信息(存于数组中)
printf("是否继续添加(yes--y,no--n)只能输入一个字符\n");
scanf(" %c",&sel); //输入时,遇到字符或字符串小心:记得输入前留空格
}
save(stu,*nptr); //将信息从数组中保存到文件中
}
/*3.1 用于实现读取一个学生信息并存入数组中*/
//依次输入 学生信息:学号,姓名,性别,五门课程成绩,总成绩,平均成绩
//小贴士:
//输入是否继续时,只能输入一个字符; 字符 y,表示继续添加;其他(一个)字符,表示不添加
void InputRecord(STU stu[], int i)
{
int j;
printf("请输入第%d个学生信息\n",i+1);
printf("请输入学生学号(学号小于9位数字)\n");//输入学号,并验证合法性
scanf(" %s",&stu[i].ID);
printf("请输入学生姓名(整体不能有空隙)\n");
scanf(" %s",&stu[i].name);
printf("请输入学生性别(f或m)其中f表示女性,m表示男性\n");
scanf(" %c",&stu[i].sex);
//输入五门课程成绩并进行累加
printf("请输入五门课程成绩(0~100)\n");
stu[i].total = 0; //先进行初始化
for(j=0 ; j < 5 ; j++)
{
scanf("%lf",&stu[i].score[j]);
stu[i].total += stu[i].score[j];
}
//平均成绩的计算
stu[i].avg = stu[i].total/5;
}
/*3.2 用于将数组中的信息保存到文件中 (n个同学)*/
//调用fwrite()函数 ,将数组中的信息存入文件中
//了解exit()函数 ,用来终止正在执行的程序
void save(STU stu[] , int n)
{
FILE *fp;
if((fp = fopen("D:\\student1.txt","w")) == NULL)
{
printf("打开文件失败\n");
exit(0);
}
fwrite(stu , n*sizeof(stu[0]) , 1,fp); //一个学生信息,用一个结构体数组存储
fclose(fp);
}
/*4.0修改指定的信息*/
//首先要输入学号,进行遍历,返回其结构体数组下标;
//接着显示修改前的信息
//然后选择修改的内容(逐项询问): 姓名,性别,成绩
//最后将数组中的信息存到文件中 (已写过save()函数)
//会用到4.1 函数:比对学号(字符串),返回结构体数组下标(定位需要修改信息的学生)
//会用到4.2 函数:输出所有学生信息
//会用到4.21函数: 输出指定学生信息
/*4.0修改*/
void edit(STU stu[] , int n)
{
int i , index; //index用来确定要修改学生信息的 下标
char sel; //一个字符,判断是否修改
char str[20]; //存学号
printf("请输入学号:\n");
scanf("%s", str);
index = FindByNum(stu,n,str); //4.1遍历结构数组stu,找到和str学号一致的信息记录下标
if(index < 0)
{
printf("该学生信息不存在\n");
return;
}
printf("修改前的该学生信息:\n");
PrintRecord(&stu[index]); //4.2显示指定学生信息
//修改姓名
printf("是否修改姓名?y/n(只可以输入一个字符):\n"); //只可以输入一个字符
scanf(" %c",&sel);
if(sel == 'y')
{
printf("请输入姓名:\n");
scanf(" %s",stu[index].name); //对于已有该内存空间,重新写入内容,便会更新
}
//修改性别
printf("是否修改性别?y/n:(只可以输入一个字符):\n");
scanf(" %c",&sel);
if(sel == 'y')
{
printf("请输入性别(f/m):\n");
scanf(" %c",&stu[index].sex);
}
//修改成绩
printf("是否修改成绩?y/n:\n");
scanf(" %c",&sel);
if(sel == 'y')
{
stu[index].total = 0;
printf("请输入5门课程成绩:\n");
for(i=0 ;i < 5; i++)
{
scanf("%lf",&stu[index].score[i]);
stu[index].total +=stu[index].score[i];
}
stu[index].avg = stu[index].total /5;
}
save(stu,n); //将修改后的信息,从数组保存到文件
}
/*4.1 比对学号(字符串),返回结构体数组下标(定位需要修改信息的学生) */
//按照学号查找信息,返回对应位置(下标)
//在所有信息中,查找到信息返回下标;否则返回负数
int FindByNum(STU stu[] , int n , char *str)
{
int i;
for(i=0; i<n ; i++)
{
if(strcmp(stu[i].ID,str)==0) //若找到便返回下标
{
return i;
}
}
return -1;
}
/*4.2 输出所有学生信息*/
//先打印出来表头
//再打印出来指定学生信息 (循环遍历)
//函数 system("pause"), 冻结屏幕,等待用户信号,
//不然控制台程序一闪而过 ,来不及看运行结果
void output(STU stu[] , int n)
{
int i;
//打印表头
printf("%8s%8s%4s%8s%8s%8s%8s%8s%8s%8s\n","ID","NAME","SEX","SCORE1",\
"SCORE2","SCORE3","SCORE4","SCORE5","total","avg");
//打印所有信息
for(i=0; i<n ;i++)
{
PrintRecord(&stu[i]);
}
system("pause");
}
/*4.21 输出指定学生信息*/
//纯输出哦
void PrintRecord(STU *sPtr)
{
int i;
printf("%8s%8s%4c",sPtr->ID,sPtr->name,sPtr->sex);
for(i=0;i<5;i++)
{
printf("%8.2lf",sPtr->score[i]);
}
printf("%8.2lf%8.2lf\n",sPtr->total,sPtr->avg);
}
/*5.0排序*/
遍历n个学生信息进行比较排序
搞清楚 函数指针的用处
进行遍历比较排序
三种排序方法,合并为一种规则(函数里边套用函数)
排序方法有 1.按总分升序,2.按总分降序,3.按姓名排序
5.1按总成绩升序ascend
5.2按总成绩降序desend
5.3按姓名升序
/*5.0排序*/
void sort(STU stu[] , int n)
{
int select, i, j;
int (*cmp)(STU a, STU b); //函数指针的介绍;(形参列表:指向的函数所带的参数列表)
//cmp是指针变量,指向带两个 STU类型形参的函数,返回值是int类型
/*
int ScoreAsc(STU a , STU b); //5.1按成绩升序ascend
int ScoreDes(STU a , STU b); //5.2按成绩降序desend
int NameAsc(STU a , STU b); //5.3按姓名升序
*/
printf("1 按分数升序排列\n");
printf("2 按分数降序排列\n");
printf("3 按姓名升序排列\n");
printf("4 其他按键会退出\n");
scanf("%d",&select);
//-------------------------------++++++++++++// 两种方法1.函数指针用switch 2.函数指针数组
switch(select)
{
case 1: cmp = ScoreAsc; break;
case 2: cmp = ScoreDes; break;
case 3: cmp = NameAsc; break;
default: return ;
}
//进行按指定规则进行 比较交换排序
for(i=0;i < n-1;i++)
{
for(j=i+1;j < n;j++)
{
if((*cmp)(stu[i],stu[j]) > 0) //判断是正数还是负数
//结构体数组的交换,不可以下标进行简单交换
{
STU temp ; //可以用结构体变量,也可以用结构体数组
temp = stu[i];
stu[i] = stu[j];
stu[j] = temp;
}
}
}
}
/*5.1按总成绩升序ascend*/
//逆序返回1,要进行交换,否则返回-1
//(条件表达式)三元运算符: A?B:C
//如果表达式A的结果为1(非零),就会求解B否则求解C,讲求出的值作为整个表达式的值
int ScoreAsc(STU a , STU b)
{
return a.total > b.total ? 1 : -1 ;
}
/*5.2按总成绩降序desend*/
//如果逆序(从小到大),返回1,会进行交换排序
int ScoreDes(STU a , STU b)
{
return a.total < b.total ? 1 : -1 ;
}
/*5.3按姓名升序*/
//若两者逆序,返回正数,将进行交换排序
int NameAsc(STU a , STU b)
{
return strcmp(a.name,b.name);
}
/*6.查找 */
按 学号 查找学生信息 ,显示指定学生信息
在所有信息中查找指定信息 已写函数:
int FindByNum(STU stu[] , int n , char *str);
函数4.1 比对学号,返回结构体数组下标(定位需要修改信息的学生)输出指定学生信息,已写函数:
void PrintRecord(STU *sPtr); //4.21 输出指定学生信息
void find(STU stu[],int n)
{
char str[20];
int index=0;
printf("请输入学号:\n");
scanf("%s",str);
index = FindByNum(stu,n,str);
if(index <0)
{
printf("\n---该学生信息不存在---\n");
}
else
{
//打印表头
printf("%8s%8s%4s%8s%8s%8s%8s%8s%8s%8s\n","ID","NAME","SEX","SCORE1",\
"SCORE2","SCORE3","SCORE4","SCORE5","total","avg");
PrintRecord(&stu[index]); //参数为指针(记得是否带取地址符)
}
system("pause");
}
/*7.删除(删除后总人数会变化,故用指针传输)*/
先输入学号明确需要删除的学生,接着在信息中找该生信息(返回下标)
用到函数4.1 int FindByNum(STU stu[] , int n , char *str);
4.1 比对学号,返回结构体数组下标(定位需要修改信息的学生)
然后进行遍历,让以后的信息覆盖前边的信息
最后记得在总人数中减去一人;
将删除后的数组中的信息,存入文件中 (函数3.2)
void save(STU stu[] , int n); //3.2 用于将数组中的信息保存到文件中 (n个同学)
void del(STU stu[] , int *nptr) //nptr用于记录总人数,删除后要记得减去
{
char str[20];
int i;
int index; //用于记录所查找学生信息在结构体数组中的下标
printf("输入需要删除学生信息的学号:\n");
scanf("%s",str);
index = FindByNum(stu,*nptr,str);
if(index < 0)
{
printf("\n---该生信息不存在---\n");
return ;
}
for(i = index;i < *nptr-1; i++)//容易出错,i=index。
// 目的是从该存储学生信息位置开始,让后边信息一次覆盖前边的信息
{
stu[i] = stu[i+1] ;
}
(*nptr)--;
printf("\n信息删除成功\n");
save(stu,*nptr); //将数组中的学生信息,存入到文件中
}
5.整体详细代码(保姆级)
#include<stdio.h> //定义了FILE结构类型名 ,文件的读写函数fread(),fwrite()
#include<string.h> //strcmp()(自己写的FindByNum()函数)
#include<ctype.h>
#include<stdlib.h> //该文件定义了1.system()函数(用在display函数)
//2.exit()函数(用在save函数)
#define N 10000
typedef struct student
{
char ID[15]; //学号
char name[25]; //姓名
char sex; //性别
double score[9]; //五门课程成绩
double total; //总成绩
double avg; //平均成绩
}STU;
//函数
void load(STU stu[],int *nptr); //1.把学生信息从文件存入到数组
void display(void); //2.显示主菜单,进行模式的选择
void add(STU stu[], int *nptr); //3.0 添加
void InputRecord(STU stu[], int i); //3.1 用于实现读取一个学生信息并存入数组中
void save(STU stu[] , int n); //3.2 用于将数组中的信息保存到文件中 (n个同学)
void edit(STU stu[] , int n); //4.0修改
int FindByNum(STU stu[] , int n , char *str);//4.1 比对学号,返回结构体数组下标(定位需要修改信息的学生)
void output(STU stu[] , int n); //4.2 输出所有学生信息
void PrintRecord(STU *sPtr); //4.21 输出指定学生信息
void sort(STU stu[] , int n); //5.0排序(以下函数可以在本函数中声明)
int ScoreAsc(STU a , STU b); //5.1按总成绩升序ascend
int ScoreDes(STU a , STU b); //5.2按总成绩降序desend
int NameAsc(STU a , STU b); //5.3按姓名升序
void find(STU stu[], int n); //6.查找
void del(STU stu[] , int *nptr);//7.删除(删除后总人数会变化,故用指针传输)
//8.输出 函数4.2
int main()
{
int n; //总人数
STU stu[N]; //结构数组
int select; //记录模式选项
load(stu,&n); //有何用1.把学生信息从文件存入到数组
//当关闭后再次编译,运行。就用上了,输出时要用数组
while(1)
{
display(); //2.显示主菜单,进行选择
scanf("%d",&select);
switch(select) //模式选择
{
case 1:
add(stu,&n); break; //3.添加
case 2:
edit(stu,n); break; //4.修改
case 3:
sort(stu,n); break; //5.排序
case 4:
find(stu,n); break; //6.查找
case 5:
del(stu,&n); break; //7.删除
case 6:
output(stu,n); break; //8.输出
case 7:
return 0; //退出
}
}
}
/*1.把学生信息从文件存入到数组 */
//可直接用 fread 函数 实现将文件中的信息转移到数组中
//也会用到文件的打开和关闭 fopen, fclose函数
//fopen函数 , 文件打开成功返回指针;文件打开失败则返回NULL
//fclose函数, 文件关闭成功返回0;文件关闭失败返回非零值
//数据块读写fread函数, 函数调用成功 返回实际读写的次数; 调用失败返回0
void load(STU stu[], int * nptr) //nptr 用于记录学生数量
{
FILE *fp;
int i;
if((fp = fopen("D:\\student1.txt","r"))==NULL)
{
*nptr=0; //nptr 用于记录学生数量
return;
}
for(i=0;fread(&stu[i],sizeof(STU),1,fp)!=0;i++)//以是否读取到信息为判断条件
*nptr=i+1; //nptr 用于记录学生数量
/* 请观察这两行代码,小提示 “;”
for(i=0;fread(&stu[i],sizeof(STU),1,fp)!=0;i++);
*nptr=i;
*/
fclose(fp);
}
/*2.显示主菜单,进行模式的选择*/
//首先知道 system()函数的用法
//实现清屏操作 "CLS"
//实现变换背景,前景颜色设置 "color XY"
//X第一个十六进制数为背景色设置,Y第二个十六进制数为前景色设置。
void display(void)
{
system("CLS");
system("color 03"); //将前景颜色设置为湖蓝色(浅淡绿色),背景颜色没有改变
printf("欢迎使用本学生信息管理系统\n");
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(" |____________________________|\n");
}
/* 3. 添加 */
//本函数需要实现:
//连续添加 任意新记录(放在数组中),添加新记录后把信息保存到文件中
//3.1 函数 InputRecord(); 用于实现读取学生信息并存入数组中
//3.2 函数 save(); 用于将数组中的信息保存到文件中
void add(STU stu[], int *nptr)//此时 nptr 仍是记录学生人数
{
int i=0;
char sel ='y';
while(sel == 'y')
{
InputRecord(stu,(*nptr)++); //添加第n+1个学生的信息(存于数组中)
printf("是否继续添加(yes--y,no--n)只能输入一个字符\n");
scanf(" %c",&sel); //输入时,遇到字符或字符串小心:记得输入前留空格
}
save(stu,*nptr); //将信息从数组中保存到文件中
}
/*3.1 用于实现读取一个学生信息并存入数组中*/
//依次输入 学生信息:学号,姓名,性别,五门课程成绩,总成绩,平均成绩
//小贴士:
//输入是否继续时,只能输入一个字符; 字符 y,表示继续添加;其他(一个)字符,表示不添加
void InputRecord(STU stu[], int i)
{
int j;
printf("请输入第%d个学生信息\n",i+1);
printf("请输入学生学号(学号小于9位数字)\n");//输入学号,并验证合法性
scanf(" %s",&stu[i].ID);
printf("请输入学生姓名(整体不能有空隙)\n");
scanf(" %s",&stu[i].name);
printf("请输入学生性别(f或m)其中f表示女性,m表示男性\n");
scanf(" %c",&stu[i].sex);
//输入五门课程成绩并进行累加
printf("请输入五门课程成绩(0~100)\n");
stu[i].total = 0; //先进行初始化
for(j=0 ; j < 5 ; j++)
{
scanf("%lf",&stu[i].score[j]);
stu[i].total += stu[i].score[j];
}
//平均成绩的计算
stu[i].avg = stu[i].total/5;
}
/*3.2 用于将数组中的信息保存到文件中 (n个同学)*/
//调用fwrite()函数 ,将数组中的信息存入文件中
//了解exit()函数 ,用来终止正在执行的程序
void save(STU stu[] , int n)
{
FILE *fp;
if((fp = fopen("D:\\student1.txt","w")) == NULL)
{
printf("打开文件失败\n");
exit(0);
}
fwrite(stu , n*sizeof(stu[0]) , 1,fp); //一个学生信息,用一个结构体数组存储
fclose(fp);
}
/*4.0修改指定的信息*/
//首先要输入学号,进行遍历,返回其结构体数组下标;
//接着显示修改前的信息
//然后选择修改的内容(逐项询问): 姓名,性别,成绩
//最后将数组中的信息存到文件中 (已写过exit()函数)
//会用到4.1 函数:比对学号(字符串),返回结构体数组下标(定位需要修改信息的学生)
//会用到4.2 函数:输出所有学生信息
//会用到4.21函数: 输出指定学生信息
void edit(STU stu[] , int n)
{
int i , index; //index用来确定要修改学生信息的 下标
char sel; //一个字符,判断是否修改
char str[20]; //存学号
printf("请输入学号:\n");
scanf("%s", str);
index = FindByNum(stu,n,str); //4.1遍历结构数组stu,找到和str学号一致的信息记录下标
if(index < 0)
{
printf("该学生信息不存在\n");
return;
}
printf("修改前的该学生信息:\n");
PrintRecord(&stu[index]); //4.2显示指定学生信息
//修改姓名
printf("是否修改姓名?y/n(只可以输入一个字符):\n"); //只可以输入一个字符
scanf(" %c",&sel);
if(sel == 'y')
{
printf("请输入姓名:\n");
scanf(" %s",stu[index].name); //对于已有该内存空间,重新写入内容,便会更新
}
//修改性别
printf("是否修改性别?y/n:(只可以输入一个字符):\n");
scanf(" %c",&sel);
if(sel == 'y')
{
printf("请输入性别(f/m):\n");
scanf(" %c",&stu[index].sex);
}
//修改成绩
printf("是否修改成绩?y/n:\n");
scanf(" %c",&sel);
if(sel == 'y')
{
stu[index].total = 0;
printf("请输入5门课程成绩:\n");
for(i=0 ;i < 5; i++)
{
scanf("%lf",&stu[index].score[i]);
stu[index].total +=stu[index].score[i];
}
stu[index].avg = stu[index].total /5;
}
save(stu,n); //将修改后的信息,从数组保存到文件
}
/*4.1 比对学号(字符串),返回结构体数组下标(定位需要修改信息的学生) */
//按照学号查找信息,返回对应位置(下标)
//在所有信息中,查找到信息返回下标;否则返回负数
int FindByNum(STU stu[] , int n , char *str)
{
int i;
for(i=0; i<n ; i++)
{
if(strcmp(stu[i].ID,str)==0) //若找到便返回下标
{
return i;
}
}
return -1;
}
/*4.2 输出所有学生信息*/
//先打印出来表头
//再打印出来指定学生信息 (循环遍历)
//函数 system("pause"), 冻结屏幕,等待用户信号,
//不然控制台程序一闪而过 ,来不及看运行结果
void output(STU stu[] , int n)
{
int i;
//打印表头
printf("%8s%8s%4s%8s%8s%8s%8s%8s%8s%8s\n","ID","NAME","SEX","SCORE1",\
"SCORE2","SCORE3","SCORE4","SCORE5","total","avg");
//打印所有信息
for(i=0; i<n ;i++)
{
PrintRecord(&stu[i]);
}
system("pause");
}
/*4.21 输出指定学生信息*/
//纯输出哦
void PrintRecord(STU *sPtr)
{
int i;
printf("%8s%8s%4c",sPtr->ID,sPtr->name,sPtr->sex);
for(i=0;i<5;i++)
{
printf("%8.2lf",sPtr->score[i]);
}
printf("%8.2lf%8.2lf\n",sPtr->total,sPtr->avg);
}
/*5.0排序*/
//遍历n个学生信息进行比较排序
//搞清楚 函数指针的用处
//进行遍历比较排序
//三种排序方法,合并为一种规则(函数里边套用函数)
//排序方法有 1.按总分升序,2.按总分降序,3.按姓名排序
//5.1按总成绩升序ascend
//5.2按总成绩降序desend
//5.3按姓名升序
/*5.0排序*/
void sort(STU stu[] , int n)
{
int select, i, j;
int (*cmp)(STU a, STU b); //函数指针的介绍;(形参列表:指向的函数所带的参数列表)
//cmp是指针变量,指向带两个 STU类型形参的函数,返回值是int类型
/*
int ScoreAsc(STU a , STU b); //5.1按成绩升序ascend
int ScoreDes(STU a , STU b); //5.2按成绩降序desend
int NameAsc(STU a , STU b); //5.3按姓名升序
*/
printf("1 按分数升序排列\n");
printf("2 按分数降序排列\n");
printf("3 按姓名升序排列\n");
printf("4 其他按键会退出\n");
scanf("%d",&select);
//-------------------------------++++++++++++// 两种方法1.函数指针用switch 2.函数指针数组
switch(select)
{
case 1: cmp = ScoreAsc; break;
case 2: cmp = ScoreDes; break;
case 3: cmp = NameAsc; break;
default: return ;
}
//进行按指定规则进行 比较交换排序
for(i=0;i < n-1;i++)
{
for(j=i+1;j < n;j++)
{
if((*cmp)(stu[i],stu[j]) > 0) //判断是正数还是负数
//结构体数组的交换,不可以下标进行简单交换
{
STU temp ; //可以用结构体变量,也可以用结构体数组
temp = stu[i];
stu[i] = stu[j];
stu[j] = temp;
}
}
}
}
/*5.1按总成绩升序ascend*/
//逆序返回1,要进行交换,否则返回-1
//(条件表达式)三元运算符: A?B:C
//如果表达式A的结果为1(非零),就会求解B否则求解C,讲求出的值作为整个表达式的值
int ScoreAsc(STU a , STU b)
{
return a.total > b.total ? 1 : -1 ;
}
/*5.2按总成绩降序desend*/
//如果逆序(从小到大),返回1,会进行交换排序
int ScoreDes(STU a , STU b)
{
return a.total < b.total ? 1 : -1 ;
}
/*5.3按姓名升序*/
//若两者逆序,返回正数,将进行交换排序
int NameAsc(STU a , STU b)
{
return strcmp(a.name,b.name);
}
/*6.查找 */
//按 学号 查找学生信息 ,显示指定学生信息
//在所有信息中查找指定信息 已写函数:
//int FindByNum(STU stu[] , int n , char *str);
//函数4.1 比对学号,返回结构体数组下标(定位需要修改信息的学生)
//输出指定学生信息,已写函数:
//void PrintRecord(STU *sPtr); //4.21 输出指定学生信息
void find(STU stu[],int n)
{
char str[20];
int index=0;
printf("请输入学号:\n");
scanf("%s",str);
index = FindByNum(stu,n,str);
if(index <0)
{
printf("\n---该学生信息不存在---\n");
}
else
{
//打印表头
printf("%8s%8s%4s%8s%8s%8s%8s%8s%8s%8s\n","ID","NAME","SEX","SCORE1",\
"SCORE2","SCORE3","SCORE4","SCORE5","total","avg");
PrintRecord(&stu[index]); //参数为指针(记得是否带取地址符)
}
system("pause");
}
/*7.删除(删除后总人数会变化,故用指针传输)*/
//先输入学号明确需要删除的学生,接着在信息中找该生信息(返回下标)
// 用到函数4.1 int FindByNum(STU stu[] , int n , char *str);
//4.1 比对学号,返回结构体数组下标(定位需要修改信息的学生)
//然后进行遍历,让以后的信息覆盖前边的信息
//最后记得在总人数中减去一人;
//将删除后的数组中的信息,存入文件中 (函数3.2)
//void save(STU stu[] , int n); //3.2 用于将数组中的信息保存到文件中 (n个同学)
void del(STU stu[] , int *nptr) //nptr用于记录总人数,删除后要记得减去
{
char str[20];
int i;
int index; //用于记录所查找学生信息在结构体数组中的下标
printf("输入需要删除学生信息的学号:\n");
scanf("%s",str);
index = FindByNum(stu,*nptr,str);
if(index < 0)
{
printf("\n---该生信息不存在---\n");
return ;
}
for(i = index;i < *nptr-1; i++)//容易出错,i=index。
// 目的是从该存储学生信息位置开始,让后边信息一次覆盖前边的信息
{
stu[i] = stu[i+1] ;
}
(*nptr)--;
printf("\n信息删除成功\n");
save(stu,*nptr); //将数组中的学生信息,存入到文件中
}
四、程序运行结果及分析
1.添加
2.修改
3.排序
4.查找
5.删除及输出
6.退出
五、遇到的问题及其创新之处
1.从不明白load( )函数的用处--->将文件中信息保存到数组中,直到自己检测该信息管理系统的输入与输出结果时,才知道在自己退出后重新进如该系统进行编译,运行会用到该函数,因为输出需要用到结构体数组
2.对display( )该显示页面进行创新及深刻理解:我将该页面的背景颜色设置为黑色,字体颜色设置成浅绿色,仅仅通过一行代码就实现了;如果想进行其他的设置也可以,需要知道C语言中#include<stdlib.h>中的system( )函数颜色的使用就可以啦!
3.当面对输入成绩,想让它达到只用输入5门课程成绩,就存储好各科成绩,总成绩以及平均成绩。这样的效果,采用结构体数组中存有五门课程成绩单独存储,平均成绩和总成绩分开存储的原则;接着进行遍历就可以达到这样的效果,可谓一举多得!
4.修改信息和添加信息的创新处:当输入相同学号会出现“该生信息已存在,修改/添加失败”,这样存储的信息就增大了准确性,可谓“大快人心”!
5.添加和修改学生信息时,都用到了通过学号来在信息(结构体数组中)找下标的函数,接着根据返回值判断是否需要接着进行,不符合便会显现“不符合要求”,退出该选项,回到主页面。
6.输出时需要注意:分两种情况
(1)未关闭该系统,所写的为指定学生输出信息的函数,输出的格式
串中形如“%15s”这样的所占宽度要够长(要和自己写的结构体里的数组结合)否则在输入后输出会与前边的数据结合到一起(错误)。
原因:主要是没有注意字符串的最后一位要存储’\n’并不会和自己设置的数组存储位数一致。
需要反思:自己平时练习(学习中),为认真学或者未认真听课,导致在做小项目时,出一个小小的错误便会让自己来修改整整半天的时间。和同学们多多探讨学习的易错点,课堂上也不要以为自己会的知识就可以不听(自己的不认真)以后会用十倍甚至百倍的精力/努力去弥补。
每个人都想做“事半功倍的,一举多得的事情”,可是平时连细节都不注意的你,又凭什么,又有什么能力和底气?希望自己真正可以避免这样的情况,当然早遇到更好呀。
(2)重新打开该系统,load( )函数中for 循环的使用,由于和自己
的写法习惯不一致,按自己的写法为何会出现“重开系统与保持开启系统的状态,再添加新信息后输出总差一个信息”?
解决:意识到想法是正确的,就是代码细节未完全把握好,for循环后直接加“;”与自己让总人数在for循环中。两者是不同的!
7.在遇到排序时,创新点是(1)函数指针用switch (2)函数指针数组二者之间的选择和使用。由于新手上路我选择的是switch(),也就是函数指针。后来研究了一下用函数指针数组,这个更简单,只需一行代码和三个函数(其中也就两行代码)!
8.删除一个学生信息时,可能遇到的错误。就是按学号删除该学生信息,当自己输入该学生学号时,输出结果竟然把其他学生信息删除了。
解决办法:首先要明确 自己所写的思路。 按照输入学号,先找到该学生信息存储在结构体数组中的下标,接着最最主要的是---->通过循环遍历所有信息(一定是从该学生开始),让后边的学生信息覆盖前边的学生信息。如果把for循环里的初始位置依然像往常标注为i=0,这样删除的学生信息总是排在第一个的学生信息,就达不到自己要删除指定学生信息的效果。
六、自我评价与总结
以上是自己对这个小项目的细节性描述,接下来进行反思:
在这次用C语言写出小项目(学生信息管理系统)做实验的道路上,自己感触颇深,有一番“千淘万漉虽辛苦,吹尽黄沙始到今”的快感!也对代码的写法产生了“我对你爱爱,爱不完的不舍”!更有在这次实验的中“反思学习道路上的缺点”,“学得主动搜索知识,信息与自己完整地修改代码和自己明显感受到自身的能力提升和进步的喜悦”!
学着从题目要求去肢解信息,转化为自己能够去实现的小函数,接着去写出主控函数,然后实现所有要达到功能的函数来满足自身的需求。
我认为这里首要进行的就是:弄清楚需要达到的功能,紧接着把主控函数构建好;重要的步骤就是:每一个功能函数的实际实现;为了让这些小小的函数达到自己满意的效果。学着去搜集知识,自己调用未使用过的函数,自己书写包含许多以往基础知识的一行又一行的代码,自己计划规定时间要完成这些函数功能,总会遇到小bug让自己修改,可能会为这些小问题而纠结所有自己不熟练的知识,也可能围着一个小问题来把其他的小问题连根拔起,其实一旦出现小bug。学着修改这样的小问题(漏洞),让自己进步,让明白知道自己的不足,让自己清楚接下来要走的路。
如今的我还不能撑起自己的野心。通过这次学生信息管理系统的实验,真真切切地明白:自己的水平与大佬们的差距,但自己不胆怯,也不会退缩,更不会一蹶不振。反而我要接着进步,去坚持走下去,去缩小与大佬们的差距。
专业课的学习漫长而又艰苦,愿你继续努力,当然在身体与任务面前要做好平衡。多多地学习优秀前辈的经验。我才愈发认识到学习的重要性了,因为我来大学不是为了混文凭的。说白了,大学关键是要学会自学!大学的老师在课堂上大多只是提纲挈领的讲授知识的,他们总是高屋建瓴的以一种居高临下的目光来讲课。这就表现在大学老师讲一节课的内容在高中老师可能要讲上一个月。大学学习其实就是课后自己结合老师的讲解在深入的学习和巩固。
而对于专业的了解更是不足,一年了依然是个井底之蛙,不了解社会科技的发展动态,不明白要从事的行业。
细想起来原因有两点:一、自己没有做更加充分和详细的计划,没有计划就会盲目,不知要做什么自然无法做好;二、网络休闲诱惑(如短视频)影响平时任务的完成,无法使自己总是保持比较良好的状态,而调整状态的时间就占去了应有的奋斗时间,执行力的不足是无法达成外在成就的主要原因。另外,大学是个熔炉,周围的人和思想在不知不觉的影响着自己,导致自己时常无法找到自己真正的模样,无法表里如一就不能充分发挥自己的能力。
因此要在今后弥补自己的不足,就要从改善自己的内在决心着手,调整好心态,让身心合一,用属于自己的东西来熏陶自己,以使自己不再迷失而能不断的努力,从而超越界限。心里上的调节是很笼统的,但却是最实在的,没有这些,一切订立的计划和下定的决心都是无用的。反思才能不断改善内心,而内心平静和理智才是持续成功的保证。在大一学年我深深体会到,一切知识的学习、新鲜事物的接触都会对自己有指导作用,就看自己是不是用心去想它、真心想用它。一旦真正结合为自己的实际行动,我想一定会有很大的提高和升华。加油吧。