数组
更加深入的探索数组
在复合数据类型一节中,我们已经了解了数组的基本定义和初始化方法。本章将进一步深入讲解数组的底层原理、常见操作、多维数组以及数组与函数的关系。
数组的内存结构与原理
数组在内存中是连续存储的,这意味着数组的所有元素在内存中是紧挨着排列的。例如,int arr[5] 会分配 5 个连续的 int 类型的空间。
这种连续性带来了两个重要特性:
- 可以通过下标快速访问任意元素,时间复杂度为 O(1)。
- 可以通过指针运算遍历数组。
第一个特性我们在之前已经提到过,第二个特性我们会在下一节指针中提到。
数组的遍历与常见操作
数组最常见的操作就是遍历和批量处理数据。在前面的章节我们已经学过了计数循环,而数组的遍历最常用的就是技术循环。让我们用传统的方式写一遍:
int arr[5] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; ++i) {
cout << arr[i] << endl;
}当然,在现代 C++ 中,还有一个更加简便的方法:
int arr[5] = {1, 2, 3, 4, 5};
for (const auto& elem : arr) {
cout << elem << endl;
}这个是 C++ 11 加入的 for-range,即基于范围的循环。这种写法不仅更加简洁,而且可以减少下标越界的风险,因为它会自动遍历整个数组的每一个元素,无需手动指定下标范围。
需要注意的是,范围 for 循环在处理原生数组时,编译器会根据数组的大小自动遍历所有元素,但如果你传递的是指针或者动态分配的数组(如 int* arr = new int[n];),则无法直接使用这种写法。
有关 for-range 的更多细节,我们后面会专门提到,这里就不过多赘述。对于新手,我仍然推荐先熟悉第一种传统的方法。后面的例子也是以第一种为例。
多维数组
数组不仅可以是一维,还可以是多维。最常见的是二维数组,可以用于表示矩阵或表格。
例如:
int matrix[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};注意
多维数组不能省略除第一维以外的数组大小:
int arr[] = {1, 2, 3, 4}; // ✅ 编译器会自动推断数组的大小
int matrix[][] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
}; // ❌ 无法编译,编译器无法推断第二维的数组大小
int matrix2[][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
}; // ✅ 只省略第一维的数组大小可以编译,编译器会自动判断第一维的数组大小数组元素的修改
数组的元素是可以修改的,只需要通过索引访问到对应的元素,然后进行赋值操作即可:
int arr[5] = {1, 2, 3, 4, 5};
arr[2] = 10; // 将第三个元素修改为10数组越界赋值会导致程序崩溃或数据异常。比如 arr[5] = 100; 是错误的,因为最大下标是 4。
示例
交换数组中的两个元素
int arr[5] = {1, 2, 3, 4, 5};
int temp = arr[0];
arr[0] = arr[4];
arr[4] = temp;
// 现在 arr[0] = 5, arr[4] = 1批量修改数组元素
把所有数组的元素都加 1:
for (int i = 0; i < 5; ++i) {
arr[i] = arr[i] + 1;
}