由ASCII码表的输出程序,我们可以认识到使用循环语句处理一组连续的数据有着巨大的优势。在更普遍的情况下,数据由一组离散的数值组成,如一组学生的考试成绩。对于这些数据的处理,有效的方式是使用循环。但前提是数据可以在循环中有序的访问。ASCII码表输出程序中,循环变量i与ASCII码相对应,对于学生成绩这样的数据,需要将其放入一个容器中,以便达到连续访问的目的,这个容器可以使用数组来实现。
定义
定义一个整型变量的语句如下: int a; 浮点数为: float b;
由此可以看出定义一个变量的通用语法是:类型 变量名;
数组是一种容器,定义时自然要表现出容器与单个变量的区别,这由下标运算符指示([]),
即:类型 数组名[元素个数];
int age[5]; // 存储5个年龄值的int数组
float score[25]; // 存储25个成绩值的float数组
char letter[23]; // 存储23个字符的char数组
定义数组时,元素的个数不能为零,且必须是一个常量值。也可以在定义的同时使用数据进行初始化。初始化使用{}语句,数据之间用’,’隔开。
int ia[5] = { 23, 24, 8, 6, 9 }; // 初绐化元素的个数不能多于数组的大小
int ib[5] = { 1, 2, 3 }; // 初始值少于数组大小时,剩余值将用0值代替(如果是浮点数,则是0.0)
int ic[5] = { 1 }; // 除第一个值外剩余值均为0
char ch[] = { 'r', 'g', 'b' }; // 定义的同是并初始化,这时可以省略数组大小,最终大小就是初始化值的个数。
特性
- 数组是存放同一类型数据的集合。如age数组只存储整型, score存储浮点数。
- 数组元素的访问采用下标运行算[],第一个元素的索引为0,最后一个元素的索引为(N-1)。
printf( "%d\n", ia[0] ); // 23
ia[0] = 44; // 修改第1个元素的值
printf( "%d\n", ia[0] ); // 44
// 输出数组ib
int i;
for ( i = 0; i < 5; i += 1 )
printf( "%d ", ib[i] );
// 反向输出数组ch(最后一个元素的下标为2)
for ( i = 2; i >= 0; i -= 1 )
printf( "%c ", ch[i] );
越界
当使用循环语句访问数组时,重要的一点就是保证元素访问不会越界。如ia只有5个元素,最后一个元素的下标是4,值为a[4]。如果你不心超出了元素范围,那程序的行为将是不确定的。C语言不保证范围的有效性检查,这一点得由我们来保障。
int main()
{
int ia[5] = { 23, 24, 8, 6, 9 };
int i;
printf( "ia: " );
for ( i = 0; i < 7; i += 1 )
printf( "%d ", ia[i] );
return 0;
}
求平均值
现在回到我们提到的求学生平均成绩的问题,只是我们将数据类型改变一下,原始成绩变为整型数组存储。
int main()
{
int score[7] = { 75, 77, 89, 92, 56, 68, 94 };
int sum = 0;
for ( int i = 0; i < 7; i += 1 )
sum += score[i];
int avg = sum / 7;
printf( "total: %d\n", sum );
printf( "average: %d\n", avg );
return 0;
}
变量sum用于存储总成绩,被初始化为0值。这是必须的,因为未初始化的值是不确定的。循环体执行完成后,总成绩为551,7位学生的平均成绩为78。有个问题需要思考一下,算术中551/7的平均值是带有小数部分的,这里为什么没有?
现在试着将学生数增大到9,除了在数组中增加成绩部分外,你还需要修改个数7为9,这在代码中有3处地方。如果学生人数再次变化,这显然是个问题。你可能想到了类似下面的代码来解决这个问题,但不幸运的是,这是错误的。
int num = 3; // 定义num维护数组元素个数
int score[num] = { 1, 2, 3 }; // 可是这是错的:元素个数必须是一个常量值。
类似于9这样的字面数据在这里称为魔数,解决的方法是把它常量化。好了,该#define登场了。