数组基础及原理
C++数组是相同类型元素的有序集合,具有固定大小,是C++中最基础的数据结构之一
一、数组的定义
数组的核心特征是相同类型和固定大小,定义格式为:
元素类型 数组名[元素个数];
1. 一维数组定义
int arr1[5]; // 定义一个包含5个int类型元素的数组
double arr2[10]; // 定义一个包含10个double类型元素的数组
char arr3[3]; // 定义一个包含3个char类型元素的数组
- 元素个数必须是编译期常量(C++11前必须是字面量或
const
变量,C++11后支持constexpr
)。 - 数组名是一个常量指针(不能被赋值,如
arr1 = arr2;
是错误的)。
2. 多维数组定义
多维数组本质是“数组的数组”,最常用的是二维数组,定义格式:
元素类型 数组名[第一维大小][第二维大小]...;
int matrix[2][3]; // 2行3列的二维int数组(本质是:包含2个元素的数组,每个元素是"3个int的数组")
double cube[2][2][2]; // 三维double数组(2×2×2)
二、数组的初始化
数组可以在定义时初始化,未初始化的全局/静态数组会被自动初始化为0,局部数组(栈上)则是随机值。
1. 一维数组初始化
-
完全初始化:显式指定所有元素
int arr[3] = {1, 2, 3}; // 元素依次为1,2,3
-
部分初始化:未指定的元素自动为0
int arr[5] = {1, 2}; // 元素为1,2,0,0,0
-
省略大小初始化:编译器根据初始化列表长度自动推断大小
int arr[] = {1, 2, 3, 4}; // 自动推断大小为4
-
字符数组初始化(字符串特殊处理):
字符串会自动添加终止符'\0'
,因此初始化时需预留位置:char str1[6] = {'h', 'e', 'l', 'l', 'o'}; // 需手动加'\0',否则不是字符串 char str2[] = "hello"; // 自动包含'\0',大小为6('h','e','l','l','o','\0')
2. 多维数组初始化
-
二维数组初始化:用嵌套大括号按行初始化
int matrix[2][3] = {{1, 2, 3}, // 第一行{4, 5, 6} // 第二行 };
-
部分初始化:未指定的元素自动为0
int matrix[2][3] = {{1}, {4, 5}}; // 结果:{{1,0,0}, {4,5,0}}
-
省略第一维大小:编译器根据初始化列表推断第一维长度
int matrix[][] = {{1,2}, {3,4}, {5,6}}; // 错误:只能省略第一维 int matrix[][2] = {{1,2}, {3,4}, {5,6}}; // 正确:第一维自动推断为3
三、数组的访问方式
数组元素通过下标或指针访问,下标从0开始(即第一个元素下标为0,最后一个为n-1
,n
为元素个数)。
1. 下标访问(最常用)
格式:数组名[下标]
int arr[3] = {10, 20, 30};
cout << arr[0]; // 访问第一个元素,输出10
arr[1] = 200; // 修改第二个元素为200
- 多维数组通过多个下标访问:
int matrix[2][3] = {{1,2,3}, {4,5,6}}; cout << matrix[1][2]; // 访问第二行第三列元素,输出6
2. 指针访问
数组名会隐式转换为指向首元素的指针(除作为sizeof
和&
的操作数外),因此可通过指针偏移访问元素:
int arr[3] = {10, 20, 30};
int* p = arr; // 等价于int* p = &arr[0]cout << *p; // 访问首元素(arr[0]),输出10
cout << *(p + 1); // 访问第二个元素(arr[1]),输出20
- 多维数组的指针访问:
二维数组int matrix[2][3]
中,matrix
是指向“包含3个int的数组”的指针,matrix + i
指向第i行,*(matrix + i) + j
指向第i行第j列元素:cout << *(*(matrix + 1) + 2); // 等价于matrix[1][2],输出6
注意:下标越界问题
C++不检查数组下标是否越界(编译通过,运行时未定义行为),越界访问可能修改其他内存数据,导致程序崩溃:
int arr[3] = {1,2,3};
cout << arr[5]; // 越界访问,结果随机(危险!)
四、数组的内存布局
数组在内存中是连续存储的,所有元素占用一段连续的内存空间,这是数组的核心特性。
1. 一维数组的内存布局
假设int arr[3] = {1,2,3}
(int占4字节),内存布局如下:
arr[0] → 地址0x1000(值1)
arr[1] → 地址0x1004(值2) // 与前一个元素相差4字节(int大小)
arr[2] → 地址0x1008(值3) // 与前一个元素相差4字节
- 相邻元素地址差 = 元素类型大小(可通过
sizeof(arr[0])
获取)。 - 数组总大小 = 元素个数 × 元素类型大小(
sizeof(arr) = 3×4 = 12
字节)。
2. 多维数组的内存布局
多维数组同样是连续存储的,按“行优先”(先存完一行再存下一行)排列。
以int matrix[2][3] = {{1,2,3}, {4,5,6}}
为例,内存布局如下:
matrix[0][0] → 0x2000(1)
matrix[0][1] → 0x2004(2)
matrix[0][2] → 0x2008(3)
matrix[1][0] → 0x200C(4) // 紧跟第一行最后一个元素
matrix[1][1] → 0x2010(5)
matrix[1][2] → 0x2014(6)
- 多维数组可看作“展开的一维数组”,
matrix[1][0]
的地址 =matrix[0][2]
的地址 + 4字节。
3. 不同存储位置的数组
- 栈上的数组(局部数组):在函数内定义,生命周期随函数结束而销毁,大小固定。
- 堆上的数组(动态数组):用
new[]
分配,需手动delete[]
释放,大小可在运行时指定:int n = 5; int* arr = new int[n]; // 运行时确定大小(堆上) delete[] arr; // 必须释放,否则内存泄漏
- 全局/静态数组:在函数外或用
static
修饰,生命周期与程序一致,自动初始化为0。
总结
- 数组是固定大小的相同类型元素集合,定义时需指定类型和大小。
- 初始化支持完全、部分、省略大小等方式,字符数组需注意
'\0'
。 - 访问通过下标或指针,需避免越界。
- 内存连续存储(一维/多维均如此),栈上数组大小固定,堆上数组大小可动态指定。
掌握数组的特性对理解更复杂的数据结构(如字符串、容器)至关重要。
数组的基本操作:
1. 数组初始化
数组初始化是创建数组并为其元素赋初始值的过程,常见方式有以下几种:
// 方法1:声明时完全初始化
int arr1[5] = {1, 2, 3, 4, 5}; // 定义包含5个int元素的数组并初始化// 方法2:部分初始化(剩余元素自动为0)
int arr2[5] = {1, 2}; // 前两个元素为1、2,后三个元素自动为0// 方法3:不指定大小,由初始化列表自动推断
int arr3[] = {10, 20, 30}; // 编译器自动推断数组大小为3// 方法4:字符数组初始化(字符串)
char str1[] = "hello"; // 自动包含终止符'\0',长度为6
char str2[6] = {'h', 'i', '\0'}; // 显式添加终止符
注意:数组大小必须是编译时常量,不能是变量。
2. 数组赋值
数组赋值包括给空数组元素赋值和修改已有元素的值:
// 给空数组赋值
int nums[4];
for (int i = 0; i < 4; i++) {nums[i] = i * 2; // 逐个元素赋值:0, 2, 4, 6
}// 修改数组元素
nums[2] = 100; // 将索引2的元素改为100,数组变为:0, 2, 100, 6// 字符数组赋值
char name[10];
name[0] = 'J';
name[1] = 'o';
name[2] = 'h';
name[3] = 'n';
name[4] = '\0'; // 字符串必须以'\0'结尾
注意:C++不支持数组整体赋值(如
arr1 = arr2
是错误的),必须逐个元素赋值。
3. 数组遍历
遍历是按顺序访问数组中的每个元素,常用方法有:
int scores[] = {85, 92, 78, 90};
int length = sizeof(scores) / sizeof(scores[0]); // 计算数组长度// 方法1:传统for循环(通过索引访问)
cout << "传统for循环遍历:";
for (int i = 0; i < length; i++) {cout << scores[i] << " ";
}// 方法2:范围for循环(C++11及以上,更简洁)
cout << "\n范围for循环遍历:";
for (int score : scores) { // 自动迭代每个元素cout << score << " ";
}
技巧:
sizeof(数组名)/sizeof(数组元素)
是计算数组长度的常用方式。
4. 查找元素
查找元素是在数组中寻找特定值或满足条件的元素,主要方法有:
int arr[] = {15, 30, 25, 40, 30};
int len = sizeof(arr) / sizeof(arr[0]);
int target = 30;// 方法1:线性查找(手动实现)
int findIndex(int arr[], int length, int target) {for (int i = 0; i < length; i++) {if (arr[i] == target) {return i; // 找到返回索引}}return -1; // 未找到返回-1
}int index = findIndex(arr, len, target);
if (index != -1) {cout << "找到元素" << target << ",索引为:" << index;
} else {cout << "未找到元素" << target;
}// 方法2:使用标准库函数find(需包含<algorithm>)
#include <algorithm>
int* found = find(arr, arr + len, target);
if (found != arr + len) { // 判断是否找到cout << "\n找到元素,索引为:" << (found - arr);
}// 方法3:查找满足条件的元素
int findGreater(int arr[], int length, int value) {for (int i = 0; i < length; i++) {if (arr[i] > value) {return i; // 返回第一个大于value的元素索引}}return -1;
}
注意事项
- 数组索引从0开始,最大索引为
数组长度-1
,访问越界会导致未定义行为。 - 数组大小固定,一旦声明无法改变,若需动态大小可使用
vector
容器。 - 数组名本质上是指向首元素的指针,作为函数参数时会退化为指针,丢失长度信息。
掌握这些基本操作是使用C++数组的基础,在实际开发中,根据需求选择合适的方法可以提高代码效率和可读性。