第六章 统计成绩
小故事:
话说逸凡逃掉了第6周星期三的课,班主任还是发觉了。期中考试后,班主任找来逸凡,没有批评,只是说帮他统计成绩。逸凡拿到一堆试卷,赶紧统计全班45人的平均分。一会儿,班主任又叫逸凡统计各个分数段的人数以及各分数段的比例。逸凡又将试卷重新翻开,一个一个地统计各个分数段的人数。
一、初次接触
前面,我们使用变量来存储数据。由于逸凡要统计全班同学的平均分,所以不可能定义那么多不同的变量来存储学生成绩。幸运的是,C语言提供了数组类型。下面,我们来学习逸凡如何完成统计平均分的。
例1:统计平均分源程序:
#include<stdio.h>
#define Max_N 100
int main()
{
int Score[Max_N];
int i;
int n;
float TotalScore=0;
float Average;
printf("请输入考生人数n(1-%d):/n",Max_N);
scanf("%d",&n);
/*判断n的大小*/
if(n>Max_N || n<1)
{
printf("n太大或太小/n");
return -1;
}
/*计算平均成绩*/
printf("请输入%d个同学成绩:/n",n);
for(i=0;i<n;i++)
{
scanf("%d",&Score[i]);
TotalScore+=Score[i];
}
Average=TotalScore/n;
printf("全班同学平均分:%4.1f/n",Average);
return 0;
}
程序运行结果:为了方便演示运行结果,这里输入10个同学成绩,计算平均分。结果如下:
程序说明:(1)在主函数main()头部,用“define”定义了处理学生成绩的最多人数(可以修改);(2)从键盘输入实际处理的学生人数n;(3)输出平均成绩时,使用了精度控制格式“%4.1f”,指定输出宽度为4,精度为1。
二、本章知识点
前面,我们使用变量来存储数据。假如要表示一个由3个元素所构成的向量,程序中可以定义三个变量a1,a2,a3来分别表示该向量的3个元素。假如有100个元素,是不是定义100个变量呢?显然,这样处理很不方便。为此,C语言提供了一种数据类型——数组类型。所谓数组,就是把具有相同类型的若干变量按有序的形式组织起来。一个数组可以分解为多个数组元素,这些数组元素可以是基本数据类型或是构造类型。因此按数组元素的类型不同,数组又可分为数值数组、字符数组、指针数组、结构体数组等各种数组。这里先介绍数值数组。
1.一维数组的定义和引用
(1)一维数组的定义
在C语言中使用数组必须先进行定义,这样才能将数组元素存储到内存地址中。
一维数组的定义格式为:
类型说明符 数组名 [常量表达式]; | int Score[Max_N]; |
格式说明:(1)类型说明符是任一种基本数据类型或构造数据类型,常见的有int,float,char等等,表示数组中所有元素都是该类型;(2)数组名是用户定义的数组标识符,比如Score[Max_N],其数组名是Score;(3)方括号中的常量表达式表示数据元素的个数,也称为数组的长度。
注意事项:(1)不能在方括号中用变量来表示元素的个数,必须在定义数组时明确数组长度。例如,可以通过define来定义数组长度,也可以直接定义为int Score[100]。(2)数组元素下标从0开始计算。例如,定义int Score[6],表示Score数组有6个整型元素,分别为Score[0], Score[1], Score[2], Score[3], Score[4], Score[5]。
(2)一维数组的引用
数组元素是组成数组的基本单元。数组元素也是一种变量,其使用方法为数组名后跟一个下标。下标表示了元素在数组中的顺序号。
数组元素的一般格式为:
数组名[下标] | Score[5] 获取数组中第6个元素 |
注意事项:(1)必须先定义数组,才能使用下标变量。在C语言中只能逐个地使用下标变量,而不能一次引用整个数组。(2)下标变量只能为整型常量,且不能超过数组长度。
(3)一维数组的初始化
给数组赋值的方法可以利用赋值语句逐个对数组元素进行赋值,还可采用初始化赋值和动态赋值的方法。
1)数组初始化赋值
数组初始化赋值是指在数组定义时给数组元素赋予初值。数组初始化是在编译阶段进行的,这样可以减少运行时间,提高效率。
初始化赋值一般格式为:
类型说明符 数组名[常量表达式]={值,值……值}; | int Score[5]={80,90,100,78,93}; |
格式说明:在{ }中的各数据值即为各元素的初值,各值之间用逗号间隔。
注意事项:
(1)可以只给部分元素赋初值。当{ }中值的个数少于元素个数时,只给前面部分元素赋值。
例如:int Score[10]={80,91,72,93,64},表示只给Score[0]~Score[4]的5个元素赋值,而后5个元素自动赋0。
(2)只能给元素逐个赋值,不能给数组整体赋值。
(3)给全部元素赋值,则在数组说明中,可以不给出数组元素的个数。
例如:int Score[5]={ 80,91,72,93,64};;
可写为:int Score[]={80,91,72,93,64};。
例2:统计各分数段比例的源程序如下:
#include<stdio.h>
#define Max_N 100
int main()
{
int Score[Max_N];
int i;
int n;
int k;
int num[11]; /*存放各分数段人数*/
int FailedNum=0;/*不及格人数*/
printf("请输入考生人数n(1-%d):/n",Max_N);
scanf("%d",&n);
/*判断n的大小*/
if(n>Max_N || n<1)
{
printf("n太大或太小/n");
return -1;
}
/*输入学生成绩*/
printf("请输入%d个同学成绩:/n",n);
for(i=0;i<n;i++)
{
scanf("%d",&Score[i]);
}
/*必须进行数组初始化,也可在定义时初始化*/
for(i=0;i<11;i++)
num[i]=0;
/*进行分数段统计*/
for(i=0;i<n;i++)
{
k=Score[i]/10;
num[k]++;
}
/*累加不及格人数*/
for(i=0;i<6;i++)
FailedNum+=num[i];
/*输出各分数段的人数及所占百分比*/
printf("全班不及格(0-59)人数为%d,占%4.1f%%/n",FailedNum,FailedNum*100.0/n);
/*重复任务尽量使用循环结构*/
for(i=6;i<=9;i++)
printf("全班%d-%d人数为%d,占%4.1f%%/n",i*10,i*10+9,num[i], num[i]*100.0/n);
printf("全班100分人数为%d,占%4.1f%%/n",num[10],num[10]*100.0/n);
return 0;
}
程序运行结果:为了方便演示运行结果,这里输入6个同学成绩,计算各分数段及百分比。
程序说明:(1)将成绩划分为11个等份:0-10,所以用成绩除以10。(2)输出百分比时,需要将整数转化为实型,参与运算,程序中采用乘以100.0,输出“%”时,需要在格式控制符中使用“%%”。
2.二维数组的定义和引用
在实际问题中有很多量是二维的或多维的,因此C语言允许构造多维数组。多维数组元素有多个下标,以标识它在数组中的位置,所以也称为多下标变量。这里只介绍二维数组,多维数组可由二维数组类推而得到。
(1)二维数组定义
二维数组一般格式是:
类型说明符 数组名[常量表达式1][常量表达式2] | int Score[45][8]; |
格式说明:(1)格式中常量表达式1表示第一维下标的长度,常量表达式2 表示第二维下标的长度。例如,二维数组Score中第一个下标值为45,表示45个学生;第二个下标值为8,表示每个学生有8门课成绩。(2)二维数组元素下标也是从0开始计算。例如,二维数组Score中元素分别为Score[0][0],Score[0][1],……,Score[0][7],Score[1][0],……,Score[1][7],……,Score[44][0],Score[44][1],……,Score[44][7]。
(2)二维数组初始化
二维数组初始化也是在类型说明时给各下标变量赋以初值。二维数组可按行分段赋值,也可按行连续赋值。
注意事项:
1)可以只对部分元素赋初值,未赋初值的元素自动取0值;
2)若对全部元素赋初值,则第一维的长度可以不给出。
例如:
int Score[3][3]={81,72,93,64,55,86,77,88,99};
可以写为:
int Score[][3]={81,72,93,64,55,86,77,88,99};
3)二维数组可以看作是由一维数组的嵌套而构成的:它的元素又是一个一维数组。
例如,数组Score可以看作是一个一维数组,它有3个学生的成绩,Score[0]、Score[1]、Score[2],每个学生的成绩又是一个包含3门课程成绩的一维数组。这样,可以将Score[0]、Score[1]、Score[2]看怍是三个一维数组的名字。上述定义的二维数组可以理解为定义了三个一维数组,即相当于定义为:
int Score[0][3],Score[1][3],Score[1][3];
因此,对于二维数组初始化赋值一些注意事项可以参照一维数组。
例3: 一个学习小组有5个人,每个人有3门课的考试成绩。问各门课程的最高分是谁?
| 大学数学 | 大学英语 | 大学语文 |
赵一 | 80 | 61 | 87 |
钱二 | 75 | 65 | 63 |
孙三 | 92 | 71 | 70 |
李四 | 89 | 78 | 76 |
王五 | 90 | 72 | 72 |
输出各门课程最高分的学生名单源程序:
#include<stdio.h>
#define Max_StuNum 100
#define Max_LessonNum 10
int main()
{
int Score[Max_StuNum][Max_LessonNum];
int StuNum=0;
int LessonNum=0;
int MaxScoreStuNo[Max_LessonNum];
int i,j;
int tempMaxScore=0;
int tempStuNo=0;
char StuName[Max_StuNum][8];
char LessonName[Max_LessonNum][100];
/*输入学生人数和课程门数*/
printf("请输入学生人数(1-%d):/n",Max_StuNum);
scanf("%d",&StuNum);
/*判断n的大小*/
if(StuNum > Max_StuNum || StuNum <1)
{
printf("学生人数太多或太少/n");
return -1;
}
printf("请输入课程门数(1-%d):/n",Max_LessonNum);
scanf("%d",&LessonNum);
if(LessonNum>Max_LessonNum || LessonNum<1)
{
printf("课程门数太多或太少/n");
return -1;
}
/*输入学生姓名和课程名称*/
printf("输入%d个学生的姓名:/n",StuNum);
for(i=0;i<StuNum;i++)
scanf("%s",StuName[i]);
printf("输入%d门课程名:/n",LessonNum);
for(i=0;i<LessonNum;i++)
scanf("%s",LessonName[i]);
/*输入每个学生的每门课程成绩*/
printf("请输入%d个学生成绩/n",StuNum);
for(i=0;i<StuNum;i++)
{ printf("请输入%s的%d门成绩:/n",StuName[i],LessonNum);
for(j=0;j<LessonNum;j++)
scanf("%d",&Score[i][j]);
}
/*保存各门课程最高分学生的序号*/
for(j=0;j<LessonNum;j++)
{
tempMaxScore=0;/*循环一次需要重新赋值*/
tempStuNo=0; /*循环一次需要重新赋值*/
for(i=0;i<StuNum;i++)
{
if(Score[i][j]>tempMaxScore)
{
tempMaxScore=Score[i][j];
tempStuNo=i;
}
}
MaxScoreStuNo[j]=tempStuNo;
}
for(j=0;j<LessonNum;j++)
printf("%s的最高分是%s/n",LessonName[j], StuName[MaxScoreStuNo[j]]);
return 0;
}
程序运行结果:输入了5个学生的姓名和3门课程名称,运行结果如下:
程序说明:(1)将每次临时最高分保存在变量tempMaxScore,并记下临时最高分学生的序号tempStuNo,一门课程处理结束时将最高学生序号保存在一维数组MaxScoreStuNo中。(2)处理完一门课程后,需要对两个临时变量tempMaxScore,tempStuNo重新赋0值,否则上次保存的结果会造成错误。
三、模仿学习
例4 实现矩阵乘法C=A*B。
解题思路:(1)三个矩阵可以用二维数组来存储,定义为:
int A_Matrix[M][N],B_Matrix[N][P],C_Matrix[M][P];
矩阵相乘公式为: ,其中i=0,……,M-1;j=0,……,P-1。
(2)本程序预先定义了最大为100×100的矩阵,具体矩阵相乘时要输入实际的行和列。这里没有比较A矩阵的列和B矩阵的行是否相等,而是直接将A矩阵的列值作为B矩阵的行值。
源程序如下:
#include <stdio.h>
#define M 100
#define N 100
#define P 100
int main()
{
int i,j,k;
int A_Matrix[M][N],B_Matrix[N][P],C_Matrix[M][P];
int New_M,New_N,New_P;
printf("请输入A矩阵行(1-%d)和列(1-%d):/n",M,N);
scanf("%d %d",&New_M,&New_N);
if( New_M<1 || New_M>100 || New_N<1 || New_N>100)
{
printf("A矩阵行和列太小或太大/n");
return -1;
}
printf("请输入B矩阵列(1-%d):/n",P);
scanf("%d",&New_P);
if( New_P<1 || New_P>100)
{
printf("B矩阵列太小或太大/n");
return -1;
}
printf("请输入%d行%d列A矩阵和%d行%d列B矩阵:/n",New_M,New_N,New_N, New_P);
for(i=0;i<New_M;i++)
{
printf("输入A矩阵第%d行的%d个数据:",i+1,New_N);
for(j=0;j<New_N;j++)
scanf("%d",&A_Matrix[i][j]);
}
for(i=0;i<New_N;i++)
{
printf("输入B矩阵第%d行%d个数据:",i+1,New_P);
for(j=0;j<New_P;j++)
scanf("%d",&B_Matrix[i][j]);
}
printf("输入的A矩阵为:/n");
for(i=0;i<New_M;i++)
{
for(j=0;j<New_N;j++)
printf("%5d",A_Matrix[i][j]);
printf("/n");
}
printf("输入的B矩阵为:/n");
for(i=0;i<New_N;i++)
{
for(j=0;j<New_P;j++)
printf("%5d",B_Matrix[i][j]);
printf("/n");
}
for(i=0;i<New_M;i++)
for(j=0;j<New_P;j++)
{ C_Matrix[i][j]=0;
for(k=0;k<New_N;k++)
C_Matrix[i][j]+=A_Matrix[i][k]*B_Matrix[k][j];
}
printf("A*B矩阵的乘积C为:/n");
for(i=0;i<New_M;i++)
{ for(j=0;j<New_P;j++)
printf("%5d",C_Matrix[i][j]);
printf("/n");
}
return 0;
}
程序运行结果:
程序说明:(1)在使用C_Matrix数组时,需要初始化。(2)用scanf函数输入数据时,如果输入太多的数据,多余的数据会传给后面的变量,所以输入数据时尽量按要求输入。