实例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)来清空缓冲区以避免这种情况,下图是改正后的结果。
总结:程序吗,总是有可以改进的地方