上一节介绍了 C 语言中的结构体,它是一种复合数据类型,有了结构体,C 语言可以应对各种复杂的数据模型,比如上一节的平行四边形问题。但是有些问题,就算是结构体,也很难解决。请看下面这个问题:
小明班级有 60 个人,期末考试出成绩后,用 C 语言找出这 60 个人的最高得分。
这当然不是什么难题,会判断两个数的大小就能解决这个问题。只不过,这 60 个人的成绩怎么用 C 语言描述呢?定义 60 个变量?这样是不是太麻烦了?就算不嫌麻烦,比较两个数大小的逻辑怎么写呢,每两个变量就得写一个 if ?
好在,C 语言有数组这个好东西
和结构体类似,数组也是一种复合数据类型,数组是由一系列相同类型的元素组成的。比如上面 60 人的成绩得分,每一个人的得分在 C 语言中都可以用 float 来定义,属于同一数据类型,所以这 60 个人的成绩得分,在 C 语言中可以定义为:
float score[60];
score 后面的 [60] 表示一共有 60 个 score 这样的(即 float 类型的)数据,所以 60 个同学的成绩得分,C 语言定义这么一个数组就可以了,并不需要定义多个变量。如果人数更多,把 60 改的更大就可以解决。
我们用方框表示数组的存储单元(元素),一系列方框在一起组成了数组。方框里面的数字是成绩得分,方框外面的数字是数组的下标,每个存储单元可以用数组名+下标访问:score[0],score[1],score[28] 等等。
注意,在定义数组时,float score[60]; 这里的 60 表示数组长度,而在访问时, score[60] 这里的 60 是指 score 数组的第 60 个元素。
和我们平常数数不同,数组元素是从“第0个”开始数的,大多数编程语言都是这么规定的。这样规定使得访问数组元素非常方便,比如 score 数组中的每个元素占 4 个字节,则 score[i] 位于从数组开头跳过 4 * i 个字节的存储位置。score[i] 也可以做左值,i 也可以是表达式:
int i = 10;
score[i+2] = 76.0;
score[3] = score[i-4];
只要确保下标都是整数,这些都是合法的。
C 语言数组如何初始化
数组的初始化结构体相似,例如:
float score[60] = {68.0, 84.2, };
如果定义数组同时初始化它,可以不指定数组长度,例如:
float score[] = {68.0, 84.2, 77.7};
这时,编译器会根据初始化信息确定 score 数组的长度为 3。不过,结构体可以互相赋值,数组却不能互相赋值:
int a[3];
int b[3] = {1,2,3};
a = b; // 非法
既然数组不能互相赋值,也就不能用数组类型作为函数的返回值。这部分内容,我打算在介绍完指针后再详解。
使用 C 语言数组解决上述问题
好了,说了这么多,来看一个实例吧,我们使用 C 语言数组来记录小明班同学成绩,然后找到最高的成绩得分:
#include <stdio.h>
int main()
{
int i = 0;
float score[6] = {68.12, 66.22, 78.54, 98.18, 60.00, 59.50};
float max_score = 0;
for(i=0; i<6; i++){
if(max_score < score[i])
max_score = score[i];
}
printf("max score is : %0.2f\n", max_score);
return 0;
}
例子只使用了 6 个成绩做演示,原理是通的。
使用 C 语言数组注意事项
使用数组下标不能超出数组的长度范围,这一点在使用变量做数组下标时尤其要注意。C编译器并不检查 score[-1] 或是 score[100] 这样的访问越界错误,编译时能顺利通过,运行时却会出错。有时候这种错误很隐蔽,发生访问越界时程序可能并不会立即崩溃,而执行到后面某个正确的语句时却有可能突然崩溃。所以,从一开始写代码时就要小心避免出问题,事后依靠调试来解决问题的成本是很高的。
现在给出例子还较早,已介绍的内容不足以解释,以后会给出例子的。
所以属于运行时错误。但有时候这种错误很隐蔽,发生访问越界时程序可能并不会立即崩溃,而执行到后面某个正确的语句时却有可能突然崩溃(在“段错误”一节中我们会看到这样的例子)。所以,从一开始写代码时就要小心避免出问题,事后依靠调试来解决问题的成本是很高的。
你可能想问:为什么 C 编译器对这么明显的错误都不做处理?理由一,这种错误并不总是显而易见的,以后会讲到通过指针而不是数组名来访问数组的情况,指针指向数组的什么位置只有运行时才知道,编译时无法检查是否越界,而运行时检查数组访问越界会影响性能,C 语言是极其重视效率的编程语言,所以干脆不检查了;理由二, C99 Rationale 指出,C语言的设计精神是:相信每个C程序员都是高手,不要阻止程序员去干他们需要干的事,高手们使用count[-1]这种技巧其实并不少见,不能当作错误。