数组是一种存储相同类型的元素的集合。数组可以是一维的,也可以是多维的。字符串是由字符组成的字符数组。
5.1 数组的定义和使用
在C语言中,数组是一种用来存储同一类型的多个元素的数据结构。数组可以存储整数、字符、浮点数等类型的数据。它可以存储一个固定大小的相同类型元素的顺序集合。
数组的声明并不是单独声明一个个的变量,而是声明一个数组变量,然后通过使用 runoob[0]、runoob[1]、...、runoob[99] 来代表一个个单独的变量。
所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。数组说明见下图5-1。
数组的定义和使用需要以下几个步骤:
- 定义数组
在C语言中,数组的定义格式为:
数据类型 数组名[数组长度]
其中,数据类型表示数组中元素的类型,数组名是用来标识数组的名称,数组长度表示数组中元素的个数。
- 初始化数组
我们可以在定义数组的同时对数组元素进行初始化,也可以在定义后使用赋值语句进行初始化。
需注意:大括号{ }之间的值的数目不能大于我们在数组声明时在方括号[ ]中指定的元素数目,即数组元素不能越界。
下图5-2为在进行数组定义时的几种初始化示例代码。
- 使用数组
可以使用下标(索引)来访问数组中的元素。数组的下标从0开始,最大下标为数组长度减1。可参考下图5-3理解数组下标及元素。
我们还是以之前的数组numbers为例进行说明。示例代码如下图5-4所示:
注意:如果数组下标越界的话,会导致访问到未定义的内存区域,可能引发程序崩溃或产生不可预测的结果。
- 获取数组长度
可以使用sizeof运算符来获取数组的长度(以字节为单位)。其示例代码见下图5-5。
在上述代码中,sizeof(numbers)表示数组所占用的总字节数,sizeof(numbers[0])表示数组元素的字节数。通过两者相除,即可得到数组的长度。
5.2 多维数组及其应用
多维数组是由多个一维数组组成的数据结构。它们可以看作是矩阵或表格。在C语言中,可以使用多维数组来存储和操作多维数据。多维数组的定义格式如下:
dataType arrayName[size1][size2]...[sizeN];
其中:dataType是数组中元素的数据类型,arrayName是数组的名称,size1、size2、...、sizeN是数组每个维度的大小。
多维数组的初始化方式与一维数组相似,只是需要嵌套使用大括号来指定每个维度的初始值。多维数组最简单的形式是二维数组。一个二维数组,在本质上,是一个一维数组的列表。例如,下面定义了一个3行4列的整数矩阵(二维数组):
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
多维数组的元素可以通过使用多个下标来访问。下标的数量和维度数相同,下标从0开始。要访问二维数组中的元素,可以使用类似arrayName[row][col]的语法。见下图5-6。
因此,数组中的每个元素是使用形式为 a[ i , j ] 的元素名称来标识的,其中 a 是数组名称,i 和 j 是唯一标识 a 中每个元素的下标。例如:要访问二维数组中的第一个元素,我们可以使用 matrix[0][0];要访问第二行第三列的元素,我们可以使用 matrix[1][2]。
注意:多维数组的内存布局是连续的,即所有元素在内存中是按一维方式存储的。即对于上面的二维数组,实际存储在内存中的方式是按照1, 2, 3, 4, 5, 6, ...的顺序排列的。
多维数组的应用非常广泛,特别是在处理矩阵、图像和表格数据等方面。通过使用多维数组,可以更方便地存储和操作这些数据。
5.3 静态数组和动态数组
在 C 语言中,有两种类型的数组:静态数组和动态数组。
1. 静态数组
静态数组是在编译时声明并分配内存空间的数组。静态数组具有固定的大小,在声明数组时需要指定数组的长度。静态数组的特点包括:
- 内存分配:在程序编译时,静态数组的内存空间就被分配好了,存储在栈上或者全局数据区。
- 大小固定:静态数组的大小在声明时确定,并且无法在运行时改变。
- 生命周期:静态数组的生命周期与其作用域相关。如果在函数内部声明静态数组,其生命周期为整个函数执行期间;如果在函数外部声明静态数组,其生命周期为整个程序的执行期间。
静态数组的声明和初始化示例:
int staticArray[5]; // 静态数组声明
int staticArray[] = {1, 2, 3, 4, 5}; // 静态数组声明并初始化
下图5-7是一个简单的静态数组实例:
以上实例中,我们声明并初始化了一个静态数组 staticArray,它包含了5个整数元素,然后我们通过sizeof运算符计算静态数组的长度,并使用循环遍历并打印数组的元素。
2. 动态数组
动态数组是在运行时通过动态内存分配函数(如malloc和calloc)手动分配内存的数组。动态数组特点如下:
- 内存分配:动态数组的内存空间在运行时通过动态内存分配函数手动分配,并存储在堆上。需要使用 malloc、calloc 等函数来申请内存,并使用 free 函数来释放内存。
- 大小可变:动态数组的大小在运行时可以根据需要进行调整。可以使用 realloc 函数来重新分配内存,并改变数组的大小。
- 生命周期:动态数组的生命周期由程序员控制。在使用动态数组时,需要注意合理地分配内存,使用完数组后需要手动释放内存,以避免内存泄漏和访问无效内存的问题。
注意:动态数组的使用需要注意内存管理的问题,确保在不再需要使用数组时释放内存,避免内存泄漏和访问无效的内存位置。
下图5-8是一个简单的动态数组使用实例:
以上实例中,我们首先声明了一个变量 size 来指定动态数组的大小。然后使用 malloc 函数为动态数组分配内存,并通过 sizeof 运算符计算所需的内存大小。最后使用循环遍历并打印动态数组的元素。在程序结束时,使用 free 函数释放动态数组所占用的内存。
注意,在使用动态数组时,需要检查内存分配是否成功(即 arr 是否为 NULL),以避免在内存分配失败时发生错误。
5.4 数组作为参数传递
在C语言中,数组是一种特殊的数据结构,它可以存储一系列相同类型的数据。当我们需要在函数中使用数组时,可以通过传递数组的地址来实现。
在函数中传递数组的方式有两种:传递数组的指针或传递数组的副本。下面分别详细说明这两种方式。
1. 传递数组的指针
通过传递数组的指针,函数可以直接访问原始数组,并对其进行修改。具体操作步骤如下所示:
- 在函数的参数列表中声明一个指针变量,用于接收数组的地址。
- 在调用函数时,将数组的名称作为实参传递给函数。
- 在函数中,可以使用指针来访问数组元素,并对其进行操作。
示例代码见下图5-9。
代码解读:在上面的示例中,我们定义了一个名为modifyArray的函数,它接受一个指针参数arr和一个整数参数size,表示数组的地址和大小。在main函数中,我们定义了一个整型数组array,并计算出数组的大小。然后我们调用modifyArray函数,并将数组array和大小size作为实参传递给它。在modifyArray函数中,我们使用指针arr来访问数组元素,并对其进行操作。
2. 传递数组的副本
当我们不希望函数对原始数组进行修改时,可以传递数组的副本给函数。具体操作步骤如下所示:
- 在函数的参数列表中声明一个数组变量,用于接收数组的副本。
- 在调用函数时,将数组的名称作为实参传递给函数。
- 在函数中,可以使用数组变量来访问数组元素,但对数组元素的修改不会影响原始数组。
示例代码见下图5-10。
代码解读:在上面的示例中,我们定义了一个名为printArray的函数,它接受一个数组参数arr和一个整数参数size,表示数组的副本和大小。在main函数中,我们定义了一个整型数组array,并计算出数组的大小。然后,我们调用printArray函数,并将数组array和大小size作为实参传递给它。在printArray函数中,我们使用数组arr来访问数组元素,并将其输出。
在C语言中,可以通过传递数组的地址或传递数组的副本来在函数中使用数组。当需要对原始数组进行修改时,可以传递数组的地址;当不希望对原始数组进行修改时,可以传递数组的副本。
5.5 数组作为返回值返回
C 语言无法直接返回一个完整的数组作为函数的参数。但是,您可以通过指定不带索引的数组名来返回一个指向数组的指针。另外,C语言不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。因此我们可以通过以下两种常用的方法实现从函数返回数组:
1. 使用静态数组
在函数内部定义一个静态数组,并在函数结束前将该数组作为返回值返回。静态数组的特点是其生存周期与程序的运行周期一致,所以可以安全地返回。但是,静态数组的大小必须在编译时确定。示例代码见下图5-11。
2. 使用动态内存分配
在函数内部使用malloc函数动态分配一块内存空间,并在函数结束前将指向该内存空间的指针作为返回值返回。这样可以返回一个动态分配的数组,其大小可以在运行时确定。示例代码见下图5-12。
注意:使用动态内存分配后,需要在使用完毕后使用free函数释放已分配的内存空间,以避免内存泄漏问题。