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)来清空缓冲区以避免这种情况,下图是改正后的结果。

总结:程序吗,总是有可以改进的地方

posted @ 2018-10-08 22:17  llccbb1  阅读(531)  评论(0编辑  收藏  举报