数组变量也是变量。数组定义的形式与简单变量类似,但需要增加有关元素个数的信息。在被定义变量名之后写一对方括号就是一个数组定义,指定元素个数的方式是在括号里写一个整型表达式。
int a[10]; //定义数组时没做初始化, //如果是外部数组或局部静态数组,元素将自动初始化为 0, //如果是自动数组,元素将不初始化,这些元素的值处在没有明确初始化的状态。 extern double a1[]; //在写数组变量的外部说明时不必写数组大小,只要在数组变量名后写一对方括号。 //这个说明通知本源文件的其他部分,有个数组a1在其他地方定义,它们的元素类型是双精度类型
数组元素个数必须能在编译时静态确定,根据这个规定,下面数组定义不合法:
void f(int n) {
int b[n]; // 此时局部数组 b 的大小依赖于函数的参数值,这个值在编译时无法确定。
}
全局数组和局部静态数组(用关键字 static)同样在程序开始执行前建立并初始化;
局部自动数组也是在程序执行进入相应函数时建立和初始化。
给数组指定初值也是在定义变量时通过附加描述给出元素初值:各元素值的表达式顺序在一对花括号里,表达式间用逗号分隔。例如:
int b[4] = {1, 1, 2, 3};
//元素初值表达式必须是常量表达式,自动数组的初始化也只允许用常量表达式。
//初始化表示中元素的个数不超过数组元素的个数。
int b1[4] = {1, 2};
//语言允许只给数组前一段元素指定初值,这时未指定值的元素将自动初始化为 0。
//因为 b1 的初值不够,b1[2]、b1[3]自动初始化为 0。
//(无论是外部数组还是自动数组)。
int b2[4] = {1, 2, 0, 0};//b1 和 b2 中各元素的值完全一样。
int fib1[] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55};
//为了编程方便,C 语言规定,在给出了所有元素初值的情况下,定义数组时可以不写大小。
//这时系统根据初始化表达式的个数自动确定数组大小。
int fib2[10] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55};
//数组 fib1 和 fib2 都是 10 个元素的整数数组,两种定义方式等价。
//前一写法能带来一点方便,因为这样能减少维护两部分描述之间一致性的麻烦,有利于程序修改。
应特别指出,这种为数组元素指定值的写法只能用在初始化时,语句里不能采用这种写法。
多维数组C 语言里也能定义两维的和更多维的数组。
double a[3][2]; //在这里把两维数组看作一维数组的数组。 //也就是说,两维数组里的每个元素都是(成员类型相同,成员个数也相同的)一维数组。 //a 包含三个元素,每个元素各是一个双精度数组,其中包含了两个 double 元素。 //人们常说 a 是 3×2 的双精度数组。有时也说 a 是 3 行 2 列的数组,这是从矩阵概念那里借用的说法。 int a1[3][2][4]; //定义了一个三维数组. //三维就不好说是二维数组的一维数组还是一维数组的二维数组了。 //准确地说,三维数组是一维数组的一维数组的一维数组。 //更多维的数组的定义也与此类似。
多维数组也可以在定义时直接初始化。
int a[3][2] = {{1, 2}, {3, 4}, {5, 6}};
//内嵌各括号里的数据将用于初始化各个成员数组。
//这里要求各括号里表达式的个数都不超过成员数组长度,数据的组数不超过成员数组的个数。
//如果给出的表达式不够,相应成员数组 的其他成分也将被自动设置为 0。
int b[3][2] = {1, 2, 3, 4, 5, 6};
//多维数组的初始化表示中也可以不写内嵌括号,采用平铺的写法。
//所列出的初始值按顺序,依次给各个成员数组的各成分置初值。
//如果初始值的个数不够,剩余成分也置 0。
//第二种形式写起来简单,但不如前一形式清晰。
int c[][2] = {{1, 2}, {3, 4}, {5, 6}};
int d[][2] = {1, 2, 3, 4, 5, 6};
一维数组采用连续顺序存储元素的方式实现。多维数组的内部表示也完全一样,同样是依次连续存放数组元素,即其中的各成员数组),而这些成员数组也按同样方式存放它们的元素。
一行(一个成员数组)的元素连续存放,这种形式又被称为按行存放,或者行优先存放。
字符数组字符数组就是以字符作为元素的数组。由于人们常用 C 语言写与处理字符序列或文本的程序,而字符序列理所当然地应该存放在字符数组中,所以C为处理字符数组提供了特别支持。
对于程序中写出的字符串字面量,系统将用字符数组的形式对其进行存储他们:分配连续的若干个存储单元,顺序存入字符串中的各个字符,每个字符占据一个字节。在存储了字符串常量的所有字符之后,还要另外存一个空字符 ' ’作为字符串的结束标志。
例如下面的字符串虽然只有7个字符,其内部却要占据8个字节的存储。
用这种方式表示字符串是为了处理方便。字符串数据与基本类型的数据不同,不同的字符串可能有不同长度。这种情况下,程序怎么才能从字符串的内部表示确定字符串结束的位置呢?
有了字符串末尾的空字符,处理字符串的程序就可以顺序检查,遇到空字符就知道遇到了字符串结束。虽然空字符不是字符串内容的一部分, 但却是字符串表示中不可缺少的部分。
标准库的字符串处理函数都是基于这种表示定义的, 我们自己写字符串处理程序时也应该遵守这种规则。
下面的问题是:能在自己定义的字符数组里存放字符串吗?回答是肯定的,根据字符串存储形式的规定,只要在数组里顺序存入所需字符,随后存一个空字符,这个字符数组里的数据就有了字符串的表现形式,这个数组也就可以当作字符串使用了。在这种情况下,人们也说这个数组里存了一个字符串。例如有下面定义:
char a[5] = {'i', 's', 'n', 'o', 't'}, //数组a里存的不是字符串,因为缺少表示串结束的空字符
。
b[5] = {'g', 'o', 'o', 'd'},
//数组 b是字符串,尽管提供的初始化表达式个数不够,
//但按C语言规定,数组剩余位置自动置为 0(字符值的 0 就是空字符),正好当作字符串结束标志。
c[5] = {'f', 'i', 'n', 'e', ' '},
//c 初始化时已在有效字符后加了一个空字符,所以它也存了一个字符串。
d[5] = {'o', 'k', ' '};
//数组 d 最后两个字符未给,同数组b一样,将自动设为空字符,对d作为字符串没有影响。
e[5] = {'o', 'k', ' ', '?', '?'};
// 作为字符数组,e 的 5 个元素分别有了值,意义很清楚。
//如果将e 中数据当作字符串看待和处理,遇到空字符就认为串已结束,后面的东西对串处理已经没意义了。
//所以,如果 e 被作为字符串处理时,那就是一个只包含两个字符的串。
为了方便使用,C 语言为字符数组提供了特殊的初始化形式:允许以字符串形式为字符数组的一系列元素指定初值。这种初始化形式应看成一般形式的简写,例如可以写:
char a1[20] = "Peking University"; //a1 的前 18 个字符指定了值,不但有明确写出的 17 个字符,还有一个作为字符串结束的空字符。 //随后部分自动用空字符填充 char a3[] = "Peking University"; //这定义了一个 18 个元素的数组,其中依次存放各字符,最后元素存入一个空字符。
如a [3],用字符串做字符数组初始化时也允许不直接给出数组元素个数。这时的数组大小规定为初始化字符串的字符数加 1,因为需要在数组最后存一个空字符。
字符数组可以用在各种需要字符串的地方。例如,如果程序里许多输出语句都用同样输出格式,一种可能方法就是定义一个公用的格式描述数组。例如下面例子:
char outform1[] = "Two reals: %f, %fn"; printf(outform1, x, y);
这样可以减少重复定义。对输入格式也可以采用同样技术。
参考书籍《从问题到程序——程序设计与C语言引论》裘宗燕著,北京大学出版社,1999.4
《从问题到程序——程序设计与C语言引论》2003,2004年修订



