这是一个C语言系列文章,如果是初学者的话,建议先行阅读之前的文章。笔者也会按照章节顺序发布。
上一篇我们讨论过函数,其中涉及到了一些数组和指针,本篇详细说明数组。
数组定义
**数组是一种集合结构,与数学种谈到的集合类似,用来存放同类型数据。**在C语言中,数组是定长的,即数组有其最大长度限制,超过限制的访问会导致程序崩溃或异常行为。
定义一个数组的一般形式如下:
数据类型 数组名[数组长度];
其中,数据类型不仅包含了之前文章中介绍的几种基础数据类型,还包含后续文章将会介绍的自定义类型、结构体类型等,数组长度为整数,数组名的命名规则与变量的命名规则一致。
下面看几种数组定义的例子:
int i_array[100]; //定义整型数组,名为i_array,数组长度为100,即可以容纳100个整数
char c_array[100]; //定义字符型数组,名为c_array,数组长度为100,即可容纳100个字符
数组声明
数组的声明与函数和变量的声明作用是一样的,是为了告诉编译器,这个数组在本文件或者其他源文件中有定义,避免当前文件编译因找不到定义而报错。
数组声明的一般形式:
数据类型 数组名[数组长度];
看似与数组定义一样,是的,的确可以写成一样的,但数组长度可以省略:
int i_array[100];
和
int i_array[];
这两个声明是等价的。
这里需要注意,如果省写数组长度,那么在使用sizeof计算i_array长度时编译器会报错。
数组初始化
在定义变量时,我们可以定义并赋初值,数组亦然,但是数组有几种定义并初始化的写法:
1.定义并给出全部初始值
int a[3] = {1, 2, 3};
或
int a[] = {1, 2, 3};
即初值用{}包裹,且每个元素间用逗号隔开。
对于同时给定初始值的定义,我们是可以省略数组长度的,因为编译器会根据初始值内容判断数组长度。
2.定义并给出部分初始值
int a[3] = {1, 2};
此时数组长度不可省写,否则将被认为数组只有两个元素。数组a的前两个元素分别是1和2,第三个元素数值未知。
3.定义字符数组
字符数组的定义赋初值写法与其他基础类型的定义有所不同
char s[6] = "hello";
char s[] = "hello";
上面这两者是等价的,即定义了一个数组长度为6的字符数组。
细心的读者可能会发现,hello只有五个字符,为什么会是6个元素长度呢?
这是因为双引号内的字符串(也是字符数组)都隐含的在最后包含了一个’\0’字符来表示字符串结束。因此这类字符串的长度都是双引号内字符个数总和加1。如果使用sizeof运算符计算s的长度,返回的数值就是6。
对于字符型数组(不管是char还是unsigned char)有一种特殊的初始化写法——定义并将数组元素值都清零:
char s[100] = {0};
unsigned char us[100] = {0};
此时,数组长度不可省略,且这里不只是初始化数组的第一个元素,而是将数组全部元素值设置成0。
这样的写法仅限于清零,如果换用别的数值,则会变成部分初始化。
数组元素
其实,数组本质上也是一片连续的内存空间,与我们在变量一篇中停车场例子很像。
事实上,数组的每一个元素单元就是一个变量。
我们再次以停车位使用为例来看下数组元素是如何被使用的。这里,我们假设举例镜头最近的停车位位置记为0,并向远处依次加一,且假定这一排有10个停车位。
那么这样一排停车位转换到C语言上,可以如下定义:
int parking_area[10];
之所以定义为整型,是因为每一种车型都可以用一个独特的整数来代替其名字,即车名与整数做了一个映射关系。
此时,一辆宝马驶入第1个车位,我们假定宝马对应整数为1000,那么代码可以写成:
parking_area[0] = 1000; //上面提到过第一个车位的位置为0
这里,我们用到了数组下标运算符。
本篇文章中,数组元素的访问都是通过下标来进行的,关于指针访问元素留待后续指针文章中讨论。
数组的下标个数与数组长度相等,且下标数值从0开始依次加1,即上例中的下标为0~9。
这时又来了一辆奔驰(对应整数记为1001),停入了第二个车位,那么代码如下:
parking_area[1] = 1001;
如果,宝马开走了(假设无车停放记为0),那么代码可以写成:
parking_area[0] = 0;
一个可编译运行的例子
#include <stdio.h>
int main(void)
{
char a[10] = {0};
int i;
for (i = 0; i < sizeof(a); ++i) {
printf("%d\n", a[i]);
}
return 0;
}
大家可以编译运行一下例子,这里是将定义数组a并将其所有元素全部清0。然后使用数组下标配合循环语句完成数组元素的遍历访问。关于循环的内容,将在后续文章中给出。
多维数组
上面讨论的内容涉及到的例子都是一维数组——即每个数组元素都是一个数值而非数组。
下面,我们就来介绍复杂一些数组形式,我们以二维数组为例进行演示。
int a[2][3];
这里我们就定义了一个二维数组a,它类似一个2行3列的表格或者矩阵。
对于这个二维数组,定义同时初始化可以使用如下方式:
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
或
int a[][3] = {{1, 2, 3}, {4, 5, 6}};
可以看到,其最外层数组包含了两个元素,这两个元素又分别是两个相同长度的一维数组。
同时,在定义同时初始化的情况下,第一维的数组长度可以省略,编译器将以初值中的元素个数自动填充。
二维数组的声明形式如下:
int a[2][3];
或
int a[][3];
即可以省写一维数组长度部分。
对于二维数组的元素访问,可以用如下形式:
a[0][1] = 10; //第1行第2列元素赋值10
a[1][2] = 99; //第2行第3列元素赋值99
对于更高维度的数组,在日常生产环境下是不多见的,其一般形式如下:
数据类型 数组名[m][n]...;
或
数据类型 数组名[m][n]... = {{{...},...},...};
特征就是:
- 是几维数组,数组名后的方括号就有几个
- 是几维数组,初值中就要嵌套几层{}数组
- 是几维数组,访问其元素时数组下标运算符([])就要有几个
- 对于二维及其以上维度的数组,在定义同时初始化时,只有第一维度的数组长度可以省略
- 对于二维及其以上维度的数组,声明时只有第一维度的数组长度可以省略
喜欢的读者可以关注码哥,也可以在下方留言评论,如果有不清楚的地方也可以私信码哥,我会第一时间回复。
感谢阅读!