作为程序员,不可避免地要处理大量相关数据。通常,数组能高效便捷地处理这种数据。本篇,我们就来说说 C 语言中数组的知识。
数组
C 语言的数组由数据类型相同的一系列元素组成。需要使用数组时,通过声明数组告诉编译器数组中内含多少元素和这些元素的类型。编译器根据这些信息正确地创建数组。
|
方括号([])表明声明的变量是数组,方括号中的数字表明数组中的元素个数。访问数组中的元素,通过使用数组下标数(也称为索引)表示数组中的各元素。数组元素的编号从 0 开始。
初始化
C 使用如下语法来初始化数组:
|
用以逗号分隔的值列表(用花括号括起来)来初始化数组,各值之间用逗号分隔。用数组前必须先初始化它。否则,编译器使用的值是内存相应位置上的现有值。如果初始化列表中缺少元素,则该位置上的元素为 0。
也就是说如果不初始化数组,数组元素和未初始化的普通变量一样,其中储存的都是垃圾值;但是,如果部分初始化数组,剩余的元素就会被初始化为 0。
如果初始化列表的项数多于数组元素个数,则会出现数组越界错误。
如果初始化数组时省略方括号中的数字,编译器会根据初始化列表中的项数来确定数组的大小。
|
我们可以使用 sizeof 运算符算出数组大小。sizeof 运算符给出它的运算对象的大小(以字节为单位)。所以 sizeof days 是整个数组的大小(以字节为单位),sizeof day[0] 是数组中一个元素的大小(以字节为单位)。整个数组的大小除以单个元素的大小就是数组元素的个数。
|
还有一种初始化数组的方法,但这种方法仅限于初始化字符数组。我们下一篇介绍。
指定初始化器
C99 增加了一个新特性: 指定初始化器(designated initializer)。利用该特性可以初始化指定的数组元素。
例如,只初始化数组中的最后一个元素。对于传统的 C 初始化语法,需要如下声明:
|
而 C99 规定,可以在初始化列表中使用带方括号的下标指明待初始化的元素:
|
关于指定初始化器,比较复杂的例子如下:
|
以上输出揭示了指定初始化器的两个重要特性:
- 如果指定初始化器后面有更多的值,如该例中的初始化列表中的片段 [4] = 31, 30, 31,那么后面这些值将被用于初始化指定元素后面的元素
- 如果再次初始化指定的元素,那么最后的初始化将会取代之前的初始化,如该例中初始化列表开始时把 days[1] 初始化为 28,但是 days[1] 又被后面的指定初始化 [1] = 29 初始化为 29
如果未指定元素大小,编译器会把数组的大小设置为足够装得下初始化的值。如:
|
staff 数组的元素个数为 9。
数组元素赋值
声明数组后,可以借助数组下标(或索引)给数组元素赋值。如:
|
注意:
- C 不允许把数组作为一个单元赋给另一个数组,除初始化以外也不允许使用花括号列表的形式赋值
- 编译器不会检查数组下标是否使用得当。在 C 标准中,使用越界下标的结果是未定义的。这意味着程序看上去可以运行,但是运行结果很奇怪,或异常中止
指针和数组
前面章节我们介绍过指针,指针提供一种以符号形式使用地址的方法。其实,数组表示法是在变相地使用指针。
数组名是数组首元素的地址。也就是说,如果 arr 是一个数组,下面的语句成立:
|
arr 和 &arr[0] 两者都是常量,在程序的运行过程中,不会改变。但是,可以把它们赋值给指针变量,然后可以修改指针变量的值,需要注意指针加上一个数时,它的值如何变化。在 C 中,指针加 1 指的是增加一个存储单元。对数组而言,这意味着加 1 后的地址是下一个元素的地址。
|
第 2 行打印的是两个数组开始的地址,下一行打印的是指针加 1 后的地址,即下一个元素的地址,分别观察不同类型的数组元素的地址偏移有何不同。(我们目前使用的系统中,short 类型占用 2 字节,double 类型占用 8 字节)
下面的等式体现了 C 语言的灵活性:
|
以上关系表明了数组和指针的关系十分密切,可以使用指针标识数组的元素和获得元素的值。从本质上看,同一个对象有两种表示法。实际上,C 语言标准在描述数组表示法时确实借助了指针。
数组作为函数参数
- 数组名作为函数形参时,在函数体内,其失去了本身的内涵,仅仅只是一个指针
- 在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改
所以,数据名作为函数形参时,其全面沦落为一个普通指针!
顺带一提,不要混淆如下代码:
|