我们要完成一个学生成绩管理系统,已经完成了主函数、增加函数、打印全部学生信息的函数、查找学生信息的函数,还有修改和删除没有讲。
那么今天就来讲一讲修改功能和删除功能的实现。
我们再看一下题目要求,主菜单如图1,输入数字进行相应的操作。按1键实现“增加”功能,按2键实现“查找”功能,按3键实现“修改”功能,按4键实现“删除”功能,按5键实现“显示所有信息”功能,按0键实现“退出”功能。
1 程序总框架
我们先看主函数代码,是程序总框架,如图1:
之前已经讲过,全局变量number在所有函数外定义,所有函数可以使用,这个程序中的全局变量number用来存放学生总数。
每增加一个学生信息,学生总数变量number加1。
同样,每删除一个学生信息,这个变量number也要减1。
2 查找功能:函数f4的实现
要修改学生信息,必须先查找有没有这个学生,所以我么们要先调用查找函数f4。
如果调用的函数f4返回值为某个整数,表示找到这个学生,这个整数也就是这个学生在数组中的下标,可以修改这个学生的信息。
如果函数f4的返回值为-1,则表示没有这个学生,就不用修改了。
删除也一样。
也即是说,不管是删除还是修改,都需要先查找有没有这个学生,所以先调用查找函数f4。
查找函数f4的代码如图2。
3 修改功能:函数f3的实现
在修改函数f3中,要先调用查找函数f4,如果返回值为-1,表示没有这个学生,不用修改了。
代码为:
如果找到(即函数f4的返回值不是-1),那么我们输入新的信息,直接修改。
因为d记录了查找函数f4的返回值,这个返回值就是要修改的学生的数组下标。所以修改就是直接修改数组元素a[d]。
完整的修改函数f3的代码:
注意对应格式控制符 %s的输入,对应的变量名a[d].name之前是没有&符号的。
4 删除功能:函数f2的实现
删除和修改一样,必须先查找有没有这个学生,就是说先调用查找函数f4。
如果调用的函数f4返回值为某个整数,表示找到这个学生,这个整数也就是这个学生在数组中的下标,可以执行删除。
如果函数f4的返回值为-1,则表示没有这个学生,就不用删除了。
所以删除先调用函数f4,返回值为-1表示找不到,显示查无此人。
如果找到,返回为为一个整数,删除的操作应该是后面的数据全部往前移一位,往前移动一位得用到循环,有些麻烦,当然也可以实现。
但是还有一个更简单的方法,直接把这个学生的学号改为-1,其他什么都不用改,也不用移动。
a[d].xh = -1 ; //直接将学号改成-1
删除函数f2的实现代码如图4:
4 打印功能:函数f5的实现(更新完善)
因为删除不是真正的删除,而是将学号变成-1,所以如果是这样改,那么还要修改函数f5,打印学生信息那个函数,打印时要加一个条件,不显示所有学号为-1的信息。
函数f5的修改如下:
5 调用函数的定义要写在被调函数的后面,否则需要有函数原型声明
如果函数f4的定义写在函数f2的后面,但在f2里面调用了f4,而此时f4并未定义(在f2后面才定义),则编译不通过,如图6:
此时有两种修改方法:
- 主调函数f2的定义写在被调用函数f4的后面,也就是说,先写f4的定义,再写f2的定义,那么在f2里调用f4就没有问题。如图7。
(2)如果主调函数(如f2)一定要调用定义在它后面的函数(如f4),那么需要在调用语句之前写上函数声明,则被调函数(如f4)的定义可以写在主调函数(如f2)的后面。
也就是说,如果f2的定义写在了f4的前面,但是f2里面又要调用f4,那么在调用语句之前写上f4的函数头,就可以了。
函数原型声明也称为函数声明,可以写在调用语句之前的任何地方,一般习惯写在所有函数的前面。
这这个函数里,先定义结构体类型STUD,再定义全局变量number,然后是5个函数的原型声明,再写主函数定义,后面再写各个函数的定义。
不管有多少的函数,也不管它是先定义后定义,只要把这个程序中除了主函数以外的所有函数都写函数原型声明放在前面,那么就不会出现图6的错误。
完整程序:
//第1部分:类型、全局变量
#include <stdio.h>
struct STUD
{ int xh; // 学号
char name[20]; // 姓名
int cj; // 成绩
};
int number=0; // 全局变量,学生总数
void f1(struct STUD a[]);
void f2(struct STUD a[]);
void f3(struct STUD a[]);
int f4(struct STUD a[]);
void f5(struct STUD a[]);
//第2部分 主函数
int main( )
{ struct STUD a[100];
int xz, f=1;
while(f==1)
{ printf("\n");
printf("******************\n");
printf("*1 增加 *\n");
printf("*2 删除 *\n");
printf("*3 修改 *\n");
printf("*4 查询 *\n");
printf("*5 打印 *\n");
printf("*0 退出 *\n");
printf("******************\n");
printf("请选择:");
scanf("%d", &xz);
switch(xz)
{ case 1: f1(a); break;
case 2: f2(a); break;
case 3: f3(a); break;
case 4: f4(a); break;
case 5: f5(a); break;
case 0: f=0; break;
}
}
}
//第3部分 增加函数
void f1(struct STUD a[]) //增加
{
int n, i;
printf("您需要增加多少个学生记录:");
scanf("%d", &n);
for(i=number;i<number+n;i++)
{
printf("请输入学号:");
scanf("%d", &a[i].xh);
printf("请输入姓名:");
scanf("%s", a[i].name);
printf("请输入成绩:");
scanf("%d", &a[i].cj);
}
number=number+n;
printf("添加成功\n");
}
//第4部分 删除函数
void f2(struct STUD a[]) //删除
{
int d=f4(a);
if(d==-1)
printf(" 查无此人,无法删除\n");
else
{
a[d].xh=-1; //直接将学号改成-1
printf("删除成功\n");
}
}
//第5部分 修改函数
void f3(struct STUD a[]) //修改
{
int d=f4(a);
if(d==-1)
printf(" 查无此人,无法修改\n");
else
{
printf("请输入新的学号、姓名、成绩:\n");
scanf("%d%s%d",&a[d].xh,a[d].name,&a[d].cj);
printf("修改成功\n");
}
}
//第6部分 查询函数
int f4(struct STUD a[]) //查询
{
int flag; int i,x; int xz;
printf("\n");
printf("请输入要修改的学号:");
scanf("%d",&x);
for(i=0;i<number;i++)
{
if(a[i].xh==x)
{
printf("找到学号为%d的学生,该生信息如下:\n",x) ;
printf("%d %s %d \n", a[i].xh,a[i].name,a[i].cj);
flag=i;
break;
}
}
if(i>=number)
{
printf("查无此人\n");
flag=-1;
}
return flag;
}
//第7部分 打印所有学生信息
void f5(struct STUD a[]) //打印
{
int xz, flag=1;
int i;
struct STUD max;
for(i=0;i<number;i++)
{
if( a[i].xh != -1 )
printf("%d %s %d\n",a[i].xh,a[i].name,a[i].cj);
}
}
上述代码可以直接复制运行。
程序运行结果如图10:
程序还可以完善,比如查询我们只是按学号查询,其实可以按姓名查询,而且可以模糊查询,比如输入“张”,那么我们可以查出所有姓名中含有“张”这个字的学生。
以及我们还可以增加一些功能,比如求出全全班平均分、找出最高分学生的信息、全班及格率等。
还有一个最重要的功能没有实现,我们每次运行这个程序,都要重新输入学生信息,比如一个班有50个学生,我今天输入完50个学生信息,关闭电脑,那么明天我再运行这个程序,又得重新输入这50个学生信息,这就是重复输入,因为没有把我们输入的信息保存。
所以,我们需要用文件,把我们输入的全班信息保存起来,这样就不用重复输入了。
文件是一个重要的知识点,后面讲。