C语言实例解析精粹学习笔记——34(用“结构”统计学生成绩)
实例34:
设学生信息包括学号、姓名和五门功课的成绩,要求编写输入输出学生信息的函数。在输入学生信息后,以学生成绩的总分从高到低顺序输出学生信息。
思路:
程序引入一个结构数组依次存储输入的学生信息,为了在一组学生信息排序时避免交换整个学生结构,另外引入一个存储下标的数组。排序过程中改变学生结构下标的顺序而不是交换整个结构。
程序代码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 #define N 2 //学生数据个数,练习时两三个就可以了。 6 #define SCORES 5 //成绩个数 7 #define NUMLEN 10 //允许的学号长度 8 9 //学生信息的结构体,包括:学号、姓名、五项成绩 10 struct std_type{ 11 char no[NUMLEN]; 12 char *name; 13 int scores[SCORES]; 14 }; 15 16 struct std_type students[N]; //定义一个学生结构体数组 17 //用于根据学生成绩对输出学生信息的先后进行排序 18 int order[N]; 19 int total[N]; 20 21 //函数读取一个学生的信息 22 int readastu(struct std_type *spt) 23 { 24 int len,j; 25 char buf[120]; 26 27 //输入学生的学号信息 28 //根据原书中的意思,是在进行错误输入时会终止,但实际上还是有些问题的 29 //目前我还没弄清楚是编译器的问题,还是程序的问题 30 //感觉像是程序的问题 31 printf("\nNumber : "); 32 if(scanf("%s",buf)==1) 33 strncpy(spt->no,buf,NUMLEN-1); 34 else 35 return 0; 36 37 //输入学生的姓名信息 38 printf("Name : "); 39 if(scanf("%s",buf)==1) 40 { 41 len = strlen(buf);//自己不清楚缓冲区是怎么变化的,strncpy好像只是复制,好像没有清除的功能 42 //那么之前输入的学号(Number)也是放在buf缓冲区中,这个学号信息是被 43 //清除了吗?是怎么清除的?(有理解上的错误,在文末有补充) 44 spt->name = (char *)malloc(len+1); 45 strcpy(spt->name,buf); 46 } 47 else 48 return 0; 49 50 //输入学生的成绩信息 51 printf("Scores(5 Number) : "); 52 for(j=0; j< SCORES; j++) 53 { 54 if(scanf("%d",spt->scores+j)!=1) 55 break; 56 } 57 if(j==0) 58 { 59 free((spt->name)); 60 return 0; 61 } 62 for(;j<SCORES;j++) 63 spt->scores[j] = 0; 64 return 1; 65 } 66 67 //函数输出一个学生的信息 68 int writeastu(struct std_type *spt) 69 { 70 int i; 71 72 printf("Number : %s\n",spt->no); 73 printf("Name : %s\n",spt->name); 74 printf("Scores : "); 75 for(i=0; i<SCORES; i++) 76 printf("%2d",spt->scores[i]); 77 printf("\n\n"); 78 } 79 80 int main() 81 { 82 int n,i,j,t; 83 char check_i; 84 85 for(n=0; n<N; n++) readastu(students+n);//此处和原书代码有些区别 86 87 //采用冒泡法对学生信息数组排序 88 for(i=0; i<N; i++) 89 { 90 order[i] = i; //预置第i个输入的学生 91 for(t=0,j=0; j<SCORES; j++) //求第i个学生的总分 92 t += students[i].scores[j]; 93 total[i] = t; 94 } 95 96 //冒泡排序 97 for(i=0; i<n-1; i++) //共扫视n-1遍 98 { 99 for(j=0; j<n-1-i; j++) 100 if(total[order[j]] < total[order[j+1]]) 101 { 102 t = order[j]; 103 order[j] = order[j+1]; 104 order[j+1] = t; 105 } 106 } 107 108 printf("\n\nDo you want to check if your inputs are correct?(Y/N):"); 109 scanf("%s",&check_i); 110 if(check_i=='y' || check_i=='Y') 111 for(j=0; j<n; j++) 112 writeastu(students+order[j]); 113 else 114 return 0; 115 116 return 0; 117 }
上述代码基本上99%是原书中的源代码,但是发现对于现在的编译器来说是有问题的(WIN7+CodeBlocks16.01),其中一些不解在注释中已标注。
一、对于学生的学号来说,并不能限制错误输入。
程序中设置的学号长度为10(NUMLEN),但实际上输入超过10个数字也是可以的,虽然输出结果会截止到10个数字。
第32行将输入的学号放入buf中,第33行将buf中前10个字符复制到结构体的no中
二、关于缓冲区的问题
41行中的注释是我的理解有点问题,
第39行的代码将我们输入的字符(学生的姓名)放入到buf中,(自己理解的)会覆盖之前输入的学号,所以缓冲区其实是没什么问题的。
三、对于输入成绩来说,也不能限制输入为5
当输入多于5个成绩时,输出会出现错误:
程序多于5个的数值会停留在缓冲区中,影响下一个学生的信息的输入,可以看下图中的例子
多于五个的成绩,02会被认为是下一个学生的学号,Ed会被认为是下一个学生的姓名;可以在第56后加一句fflush(stdin)来清空缓冲区以避免这种情况,下图是改正后的结果。
总结:程序吗,总是有可以改进的地方