当前位置:嗨网首页>书籍在线阅读

03-初始化数组

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

10.1.1 初始化数组

数组通常被用来存储程序需要的数据。例如,一个内含12个整数元素的数组可以存储12个月的天数。在这种情况下,在程序一开始就初始化数组比较好。下面介绍初始化数组的方法。

只存储单个值的变量有时也称为标量变量(scalar variable),我们已经很熟悉如何初始化这种变量:

int fix = 1;
float flax = PI * 2;

代码中的 PI 已定义为宏。C使用新的语法来初始化数组,如下所示:

int main(void)
{
     int powers[8] = {1,2,4,6,8,16,32,64}; /* 从ANSI C开始支持这种初始化 */
     ...
}

如上所示,用以逗号分隔的值列表(用花括号括起来)来初始化数组,各值之间用逗号分隔。在逗号和值之间可以使用空格。根据上面的初始化,把 1 赋给数组的首元素( powers[0] ),以此类推(不支持ANSI的编译器会把这种形式的初始化识别为语法错误,在数组声明前加上关键字 static 可解决此问题。第12章将详细讨论这个关键字)。

程序清单10.1演示了一个小程序,打印每个月的天数。

程序清单10.1  day_mon1.c 程序

/* day_mon1.c -- 打印每个月的天数 */
#include <stdio.h>
#define MONTHS 12
int main(void)
{
     int days[MONTHS] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
     int index;
     for (index = 0; index < MONTHS; index++)
          printf("Month %2d has %2d days.\n", index + 1, days[index]);
     return 0;
}

该程序的输出如下:

Month 1 has 31 days.
Month 2 has 28 days.
Month 3 has 31 days.
Month 4 has 30 days.
Month 5 has 31 days.
Month 6 has 30 days.
Month 7 has 31 days.
Month 8 has 31 days.
Month 9 has 30 days.
Month 10 has 31 days.
Month 11 has 30 days.
Month 12 has 31 days.

这个程序还不够完善,每4年打错一个月份的天数(即,2月份的天数)。该程序用初始化列表初始化 days[] ,列表(用花括号括起来)中用逗号分隔各值。

注意该例使用了符号常量 MONTHS 表示数组大小,这是我们推荐且常用的做法。例如,如果要采用一年13个月的记法,只需修改 #define 这行代码即可,不用在程序中查找所有使用过数组大小的地方。

注意 使用const声明数组 有时需要把数组设置为只读。这样,程序只能从数组中检索值,不能把新值写入数组。要创建只读数组,应该用 const 声明和初始化数组。因此,程序清单10.1中初始化数组应改成:

const int days[MONTHS] = {31,28,31,30,31,30,31,31,30,31,30,31};

这样修改后,程序在运行过程中就不能修改该数组中的内容。和普通变量一样,应该使用声明来初始化 const 数据,因为一旦声明为 const ,便不能再给它赋值。明确了这一点,就可以在后面的例子中使用 const 了。

如果初始化数组失败怎么办?程序清单10.2演示了这种情况。

程序清单10.2  no_data.c 程序

/* no_data.c -- 为初始化数组 */
#include <stdio.h>
#define SIZE 4
int main(void)
{
     int no_data[SIZE];  /* 未初始化数组 */
     int i;
     printf("%2s%14s\n",    "i", "no_data[i]");
     for (i = 0; i < SIZE; i++)
          printf("%2d%14d\n", i, no_data[i]);
     return 0;
}

该程序的输出如下(系统不同,输出的结果可能不同):

i     no_data[i]
0            0
1        4204937
2        4219854
3     2147348480

使用数组前必须先初始化它。与普通变量类似,在使用数组元素之前,必须先给它们赋初值。编译器使用的值是内存相应位置上的现有值,因此,读者运行该程序后的输出会与该示例不同。

注意 存储类别警告 数组和其他变量类似,可以把数组创建成不同的存储类别(storage class)。第12章将介绍存储类别的相关内容,现在只需记住:本章描述的数组属于自动存储类别,意思是这些数组在函数内部声明,且声明时未使用关键字 static 。到目前为止,本书所用的变量和数组都是自动存储类别。 在这里提到存储类别的原因是,不同的存储类别有不同的属性,所以不能把本章的内容推广到其他存储类别。对于一些其他存储类别的变量和数组,如果在声明时未初始化,编译器会自动把它们的值设置为0。

初始化列表中的项数应与数组的大小一致。如果不一致会怎样?我们还是以上一个程序为例,但初始化列表中缺少两个元素,如程序清单10.3所示:

程序清单10.3  somedata.c 程序

/* some_data.c -- 部分初始化数组 */
#include <stdio.h>
#define SIZE 4
int main(void)
{
     int some_data[SIZE] = { 1492, 1066 };
     int i;
     printf("%2s%14s\n",    "i", "some_data[i]");
     for (i = 0; i < SIZE; i++)
          printf("%2d%14d\n", i, some_data[i]);
     return 0;
}

下面是该程序的输出:

i some_data[i]
0         1492
1         1066
2            0
3            0

如上所示,编译器做得很好。当初始化列表中的值少于数组元素个数时,编译器会把剩余的元素都初始化为 0 。也就是说,如果不初始化数组,数组元素和未初始化的普通变量一样,其中存储的都是垃圾值;但是,如果部分初始化数组,剩余的元素就会被初始化为 0

如果初始化列表的项数多于数组元素个数,编译器可没那么仁慈,它会毫不留情地将其视为错误。但是,没必要因此嘲笑编译器。其实,可以省略方括号中的数字,让编译器自动匹配数组大小和初始化列表中的项数(见程序清单10.4)

程序清单10.4  day_mon2.c 程序

/* day_mon2.c -- 让编译器计算元素个数 */
#include <stdio.h>
int main(void)
{
     const int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31 };
     int index;
     for (index = 0; index < sizeof days / sizeof days[0]; index++)
          printf("Month %2d has %d days.\n", index + 1, days[index]);
     return 0;
}

在程序清单10.4中,要注意以下两点。

  • 如果初始化数组时省略方括号中的数字,编译器会根据初始化列表中的项数来确定数组的大小。
  • 注意 for 循环中的测试条件。由于人工计算容易出错,所以让计算机来计算数组的大小。 sizeof 运算符给出它的运算对象的大小(以字节为单位)。所以 sizeof days 是整个数组的大小(以字节为单位), sizeof days[0] 是数组中一个元素的大小(以字节为单位)。整个数组的大小除以单个元素的大小就是数组元素的个数。

下面是该程序的输出:

Month 1 has 31 days.
Month 2 has 28 days.
Month 3 has 31 days.
Month 4 has 30 days.
Month 5 has 31 days.
Month 6 has 30 days.
Month 7 has 31 days.
Month 8 has 31 days.
Month 9 has 30 days.
Month 10 has 31 days.

我们的本意是防止初始化值的个数超过数组的大小,让程序找出数组大小。我们初始化时用了10个值,结果就只打印了10个值!这就是自动计数的弊端:无法察觉初始化列表中的项数有误。

还有一种初始化数组的方法,但这种方法仅限于初始化字符数组。我们在下一章中介绍。