1月25日 C Primer Plus学习
10.1 数组
数组由数据类型相同的一系列元素组成。需要使用数组时,通过声明数组告诉编译器数组中内含多少元素和这些元素的类型。编译器根据这些信息正确地创建数组。普通变量可以使用的类型,数组元素都可以用。
要访问数组中的元素,通过使用数组下标数(也称为索引)表示数组中的各元素。数组元素的编号从0开始。
10.1.1 初始化数组
数组通常被用来储存程序需要的数据。
用以逗号分隔的值列表(用花括号括起来)来初始化数组,各值之间用逗号分隔。在逗号和值之间可以使用空格。
int main(void)
{
int powers[8] = {1,2,4,6,8,16,32,64}; /* 从ANSI C开始支持这种初始化 */
...
}
用以逗号分隔的值列表(用花括号括起来)来初始化数组,各值之间用逗号分隔。在逗号和值之间可以使用空格。根据上面的初始化,把 1 赋给数组的首元素(powers[0]),以此类推(不支持ANSI的编译器会把这种形式的初始化识别为语法错误,在数组声明前加上关键字static可解决此问题)。
使用数组前必须先初始化它。与普通变量类似,在使用数组元素之前,必须先给它们赋初值。
数组和其他变量类似,可以把数组创建成不同的存储类别。
当初始化列表中的值少于数组元素个数时,编译器会把剩余的元素都初始化为0。也就是说,如果不初始化数组,数组元素和未初始化的普通变量一样,其中储存的都是垃圾值;但是,如果部分初始化数组,剩余的元素就会被初始化为0。
如果初始化列表的项数多于数组元素个数,编译器可没那么仁慈,它会毫不留情地将其视为错误。但是,没必要因此嘲笑编译器。其实,可以省略方括号中的数字,让编译器自动匹配数组大小和初始化列表中的项数。
10.1.2 指定初始化器(C99)
C99 增加了一个新特性:指定初始化器(designated initializer)。利用该特性可以初始化指定的数组元素。例如,只初始化数组中的最后一个元素。对于传统的C初始化语法,必须初始化最后一个元素之前的所有元素,才能初始化它:
int arr[6] = {0,0,0,0,0,212}; // 传统的语法
而C99规定,可以在初始化列表中使用带方括号的下标指明待初始化的元素:
int arr[6] = {[5] = 212}; // 把arr[5]初始化为212
对于一般的初始化,在初始化一个元素后,未初始化的元素都会被设置为0。
如果指定初始化器后面有更多的值,那么后面这些值将被用于初始化指定元素后面的元素。
如果再次初始化指定的元素,那么最后的初始化将会取代之前的初始化。
10.1.3 给数组元素赋值
声明数组后,可以借助数组下标(或索引)给数组元素赋值。
10.1.4 数组边界
在使用数组时,要防止数组下标超出边界。也就是说,必须确保下标是有效的值。
编译器不会检查数组下标是否使用得当。在C标准中,使用越界下标的结果是未定义的。这意味着程序看上去可以运行,但是运行结果很奇怪,或异常中止。
不检查边界,C 程序可以运行更快。编译器没必要捕获所有的下标错误,因为在程序运行之前,数组的下标值可能尚未确定。因此,为安全起见,编译器必须在运行时添加额外代码检查数组的每个下标值,这会降低程序的运行速度。C 相信程序员能编写正确的代码,这样的程序运行速度更快。但并不是所有的程序员都能做到这一点,所以就出现了下标越界的问题。
10.1.5 指定数组的大小
在C99标准之前,声明数组时只能在方括号中使用整型常量表达式。所谓整型常量表达式,是由整型常量构成的表达式。sizeof表达式被视为整型常量,但是(与C++不同)const值不是。另外,表达式的值必须大于0:
int n = 5;
int m = 8;
float a1[5]; // 可以
float a2[5*2 + 1]; //可以
float a3[sizeof(int) + 1]; //可以
float a4[-4]; // 不可以,数组大小必须大于0
float a5[0]; // 不可以,数组大小必须大于0 float a6[2.5]; // 不可以,数组大小必须是整数
float a7[(int)2.5]; // 可以,已被强制转换为整型常量
float a8[n]; // C99之前不允许 float a9[m]; // C99之前不允许
10.2 多维数组
10.2.1 初始化二维数组
初始化二维数组是建立在初始化一维数组的基础上。首先,初始化一维
数组如下:
sometype ar1[5] = {val1, val2, val3, val4, val5};
这里,val1、val2等表示sometype类型的值。例如,如果sometype是int,那么val1可能是7;如果sometype是double,那么val1可能是11.34,诸如此类。但是rain是一个内含5个元素的数组,每个元素又是内含12个float类型元素的数组。所以,对rain而言,val1应该包含12个值,用于初始化内含12个 float类型元素的一维数组,如下所示:
{4.3,4.3,4.3,3.0,2.0,1.2,0.2,0.2,0.4,2.4,3.5,6.6}
也就是说,如果sometype是一个内含12个double类型元素的数组,那么 val1就是一个由12个double类型值构成的数值列表。因此,为了初始化二维数组rain,要用逗号分隔5个这样的数值列表:
const float rain[YEARS][MONTHS] =
{
{4.3,4.3,4.3,3.0,2.0,1.2,0.2,0.2,0.4,2.4,3.5,6.6},
{8.5,8.2,1.2,1.6,2.4,0.0,5.2,0.9,0.3,0.9,1.4,7.3},
{9.1,8.5,6.7,4.3,2.1,0.8,0.2,0.2,1.1,2.3,6.1,8.4},
{7.2,9.9,8.4,3.3,1.2,0.8,0.4,0.0,0.6,1.7,4.3,6.2},
{7.6,5.6,3.8,2.8,3.8,0.2,0.0,0.0,0.0,1.3,2.6,5.2}
};
这个初始化使用了5个数值列表,每个数值列表都用花括号括起来。第1 个列表的数据用于初始化数组的第1行,第2个列表的数据用于初始化数组的第2行,以此类推。前面讨论的数据个数和数组大小不匹配的问题同样适用于这里的每一行。也就是说,如果第1个列表中只有10个数,则只会初始化数组第1行的前10个元素,而最后两个元素将被默认初始化为0。如果某列表中的数值个数超出了数组每行的元素个数,则会出错,但是这并不会影响其他行的初始化。
初始化时也可省略内部的花括号,只保留最外面的一对花括号。只要保证初始化的数值个数正确,初始化的效果与上面相同。但是如果初始化的数值不够,则按照先后顺序逐行初始化,直到用完所有的值。后面没有值初始化的元素被统一初始化为0。图10.2演示了这种初始化数组的方法。

因为储存在数组rain中的数据不能修改,所以程序使用了const关键字声明该数组。
10.2.2 其他多维数组
前面讨论的二维数组的相关内容都适用于三维数组或更多维的数组。可以把一维数组想象成一行数据,把二维数组想象成数据表,把三维数组想象成一叠数据表。
通常,处理三维数组要使用3重嵌套循环,处理四维数组要使用4重嵌套循环。对于其他多维数组,以此类推。在后面的程序示例中,我们只使用二维数组。
编程练习第4&5题
代码
点击查看
#include<stdio.h>
int getMaxIndex(double num[], int i);
double calculation(double num[], int i);
void main(void)
{
double num[8] = {4.5, 3.2, 6.4, 8.9, 3.7, 9.6, 6.8, 5.9};
printf("最大值的下标为%d\n最大值与最小值的差值为%.1lf", getMaxIndex(num, 8), calculation(num, 8));
return;
}
int getMaxIndex(double num[], int i)//获取最大值的下标
{
double numMax = num[0];
int index = 0;
for (int j = 1; j < i; j++)
{
if (num[j] > numMax)
{
numMax = num[j];
index = j;
}
}
return index;
}
double calculation(double num[], int i)
{
double numMin = num[0];
for (int j = 1; j < i; j++)
{
if (numMin > num[j])
{
numMin = num[j];
}
}
return num[getMaxIndex(num, 8)]-numMin;
}
运行结果

浙公网安备 33010602011771号