一、数组的基本概念
1. 什么是数组
数组是一种线性数据结构,用于存储相同类型元素的集合。数组中的每个元素都可以通过索引(下标)来访问。
2. 数组的特点
-
相同类型:数组中的所有元素必须是相同的数据类型
-
连续内存:数组元素在内存中是连续存储的
-
固定大小:数组一旦声明,其大小就固定不变
-
下标访问:通过从0开始的索引访问元素
二、数组的声明与初始化
1. 数组声明语法
数据类型 数组名[数组大小];
示例:
int numbers[10]; // 声明一个包含10个整数的数组 float scores[5]; // 声明一个包含5个浮点数的数组 char letters[26]; // 声明一个包含26个字符的数组
1.2 数组的初始化
数组可以在定义时进行初始化:
- 完全初始化:
int arr[5] = {1, 2, 3, 4, 5}; // 为每个元素赋值
- 部分初始化:
int arr[5] = {1, 2, 3}; // 前3个元素赋值,其余元素自动初始化为0
- 省略长度的初始化:
int arr[] = {1, 2, 3, 4, 5}; // 编译器会根据初始化元素个数自动确定数组长度为5
- 字符数组的初始化(字符串):
char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; // 以空字符结尾
char str2[] = "Hello"; // 更简洁的方式,自动添加空字符
注意:用字符串初始化字符数组时,编译器会自动在末尾添加空字符 '\0' 作为字符串结束标志,因此数组长度应至少比字符串长度多 1。
2.数组元素的访问
数组元素通过索引(下标)来访问,C 语言数组的索引从 0 开始:
int arr[5] = {10, 20, 30, 40, 50};
printf("%d\n", arr[0]); // 输出第一个元素:10
printf("%d\n", arr[2]); // 输出第三个元素:30
也可以通过索引修改数组元素的值:
arr[3] = 100; // 将第四个元素修改为100
3. 数组的遍历
通常使用循环来遍历数组中的所有元素:
int arr[5] = {1, 2, 3, 4, 5};
int i;
// 使用for循环遍历数组
for(i = 0; i < 5; i++)
{
printf("arr[%d] = %d\n", i, arr[i]);
}
4.多维数组
C 语言支持多维数组,最常用的是二维数组,可以理解为 "数组的数组"。
4.1 二维数组的定义与初始化
// 定义一个3行4列的二维数组
int matrix[3][4];
// 初始化二维数组
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 也可以写成一行
int matrix[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
// 部分初始化,未初始化的元素为0
int matrix[3][4] = {{1}, {5, 6}, {9, 10, 11}};
// 可以省略第一维的大小,编译器会自动计算
int matrix[][4] = {{1,2}, {3,4,5}, {6}};
4.2 二维数组的访问与遍历
int matrix[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
// 访问单个元素
printf("%d\n", matrix[1][2]); // 输出第二行第三列的元素:7
// 遍历二维数组
int i, j;
for(i = 0; i < 3; i++) {
for(j = 0; j < 4; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
5. 数组与指针的关系
在 C 语言中,数组名本质上是一个指向数组第一个元素的指针常量。
int arr[5] = {1, 2, 3, 4, 5};
// 以下两种写法等价
printf("%d\n", arr[0]); // 使用数组表示法
printf("%d\n", *arr); // 使用指针表示法
// 访问第二个元素
printf("%d\n", arr[1]); // 数组表示法
printf("%d\n", *(arr + 1)); // 指针表示法
注意:数组名是指针常量,不能被赋值,而指针变量可以被重新赋值指向其他元素。
6.变长数组(Variable-Length Arrays, VLA)
C99 标准引入了变长数组,允许数组的长度在运行时确定:
#include <stdio.h>
int main() {
int n;
printf("请输入数组长度: ");
scanf("%d", &n);
int arr[n]; // 变长数组,长度由用户输入决定
// 初始化数组
for(int i = 0; i < n; i++) {
arr[i] = i + 1;
}
// 打印数组
for(int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
return 0;
}
变长数组的特点:
- 长度在运行时确定,可以是变量
- 不能进行初始化(因为初始化在编译时进行)
- 通常用于需要动态确定数组大小的场景
- 并非所有编译器都完全支持(如某些老版本的 MSVC 不支持)
7.数组常见错误与注意事项
-
数组越界访问:访问超出数组定义范围的索引,这是 C 语言中最常见的错误之一。
int arr[5] = {1, 2, 3, 4, 5};
printf("%d", arr[10]); // 错误:访问越界,结果不可预测
-
修改数组名:数组名是指针常量,不能被修改。
int arr[5]
; arr = arr + 1; // 错误:数组名不能被赋值
-
函数参数中数组长度的误解:在函数参数中,数组声明中的长度被忽略,实际传递的是指针。
// 以下两个函数声明等价
void func(int arr[5]);
void func(int arr[]);
void func(int *arr);
-
字符串结束符:字符数组作为字符串使用时,必须以 '\0' 结尾,否则字符串处理函数可能无法正确工作。
三、数组作为函数参数
数组可以作为函数参数传递,用于在函数中处理数组数据。
8.1 数组作为参数的传递方式
数组作为函数参数时,实际传递的是数组的首地址(指针),而不是整个数组的副本。函数声明方式有以下几种:
void func (int arr []); // 常用形式
void func (int arr [5]); // 数组长度被忽略,与上面等价
void func (int *arr); // 指针形式,与上面等价
8.2 传递数组长度
由于数组作为参数传递时丢失了长度信息,因此通常需要同时传递数组长度:
void printArray(int arr[], int length) {
for (int i = 0; i < length; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
8.3 数组参数的特点
- 函数内部修改数组元素会影响原数组(因为传递的是地址)
- 函数参数中的数组声明不分配内存空间
- 不能通过 sizeof 运算符获取原数组的长度(只能得到指针的大小)
四、数组的常见错误与注意事项
-
数组越界访问
访问索引超出数组有效范围(小于 0 或大于等于数组长度),这会导致未定义行为,可能破坏其他变量数据或导致程序崩溃。 -
误用数组名
数组名是指针常量,不能对其进行赋值操作:
int arr [5];
arr = arr + 1; // 错误,数组名不能被赋值 -
字符串处理中的错误
字符数组作为字符串使用时,忘记添加结束符 '\0',会导致字符串处理函数(如 printf、strlen 等)无法正确识别字符串结束位置。 -
函数参数中数组长度的误解
误认为函数参数中的数组声明指定了长度,实际上该长度会被编译器忽略:void func (int arr [10]) {
// 这里 arr 实际上是指针,不是长度为 10 的数组
} -
多维数组作为函数参数
传递多维数组时,除第一维外,其他维度的大小必须指定:void func (int matrix [][4], int rows) { // 正确,指定了列数
// 处理数组
}
五、数组的应用场景
数组在 C 语言编程中有广泛的应用,常见场景包括:
- 存储和处理批量数据,如学生成绩、商品价格等
- 实现其他数据结构,如栈、队列、哈希表等
- 表示矩阵、图像等二维数据
- 处理字符串(字符数组)
- 作为缓冲区存储输入输出数据
总结
数组是 C 语言中处理批量数据的重要工具,掌握数组的使用对于 C 语言编程至关重要。本文介绍了数组的定义、初始化、访问、遍历、多维数组、数组与指针的关系、数组作为函数参数等内容,并提供了实际应用示例。
理解数组的工作原理和使用技巧,能够帮助你编写更高效、更清晰的 C 语言程序。在实际编程中,要特别注意避免数组越界等常见错误,确保程序的正确性和稳定性。