在C++中,数组是存储固定大小同类型元素的连续内存块。它是最基础的数据结构之一,广泛用于各种场景。以下是关于数组的详细介绍:
一、一维数组
1. 定义与初始化
- 语法:
类型 数组名[元素个数];
- 示例:
int arr[5]; // 定义包含5个整数的数组 arr[0] = 10; // 赋值 arr[1] = 20; // 初始化列表 int arr2[5] = {1, 2, 3, 4, 5}; // 完整初始化 int arr3[] = {10, 20, 30}; // 自动推导长度为3 int arr4[5] = {1, 2}; // 部分初始化,剩余元素为0
2. 访问与遍历
- 下标访问:索引从0开始,范围为
0
到长度-1
。int value = arr[2]; // 访问第3个元素 arr[3] = 40; // 修改第4个元素
- 遍历方式:
// 传统for循环 for (int i = 0; i < 5; i++) { cout << arr[i] << " "; } // 范围for循环(C++11起) for (int num : arr) { cout << num << " "; }
二、多维数组
1. 二维数组
- 定义:
类型 数组名[行数][列数];
- 示例:
int matrix[2][3] = { {1, 2, 3}, {4, 5, 6} }; // 访问元素 int value = matrix[1][2]; // 第二行第三列(值为6)
2. 多维数组内存布局
- 连续存储:多维数组在内存中按行优先(Row-major)存储,例如:
int arr[2][2] = {{1, 2}, {3, 4}}; // 内存布局:1, 2, 3, 4
三、数组与指针
1. 数组名隐式转换为指针
- 数组名在多数表达式中会隐式转换为指向首元素的指针:
int arr[5] = {1, 2, 3, 4, 5}; int* ptr = arr; // 等价于 &arr[0] // 通过指针访问数组 cout << *ptr; // 输出1 cout << *(ptr+2); // 输出3(等价于 arr[2])
2. 指针算术
- 指针加减运算基于元素类型大小:
int* p = arr; p++; // 指针移动4字节(int类型大小)
3. 动态数组(使用指针)
- 堆上分配:使用
new
和delete[]
:int size = 10; int* dynamicArr = new int[size]; // 动态分配数组 // 使用数组 for (int i = 0; i < size; i++) { dynamicArr[i] = i; } delete[] dynamicArr; // 释放内存
四、数组作为函数参数
1. 数组退化问题
- 数组作为参数时会退化为指针,丢失大小信息:
void printArray(int arr[]) { // 等价于 int* arr // sizeof(arr) 返回指针大小(如8字节),非数组大小 }
2. 正确传递数组的方法
- 显式传递大小:
void printArray(int arr[], int size) { for (int i = 0; i < size; i++) { cout << arr[i] << " "; } }
- 使用引用:保留数组大小信息:
void printArray(int (&arr)[5]) { // 仅接受长度为5的数组 for (int num : arr) { cout << num << " "; } }
- 使用模板:自动推导大小:
template <size_t N> void printArray(int (&arr)[N]) { for (int num : arr) { cout << num << " "; } }
五、C++标准库替代方案
1. std::array
(C++11起)
- 特点:固定大小数组,封装在类中,提供更安全的接口。
- 示例:
#include <array> std::array<int, 5> arr = {1, 2, 3, 4, 5}; // 安全特性 cout << arr.size(); // 返回5 // arr[10] = 0; // 越界访问(运行时可能崩溃) arr.at(10) = 0; // 越界访问(抛出std::out_of_range异常)
2. std::vector
- 特点:动态数组,支持自动扩容。
- 示例:
#include <vector> std::vector<int> vec = {1, 2, 3}; vec.push_back(4); // 动态添加元素 cout << vec.size(); // 返回4
六、常见陷阱与最佳实践
1. 数组越界访问
- 问题:访问超出数组边界的元素,导致未定义行为。
- 示例:
int arr[3] = {1, 2, 3}; cout << arr[3]; // 越界访问,可能崩溃或读取随机值
2. 内存泄漏(动态数组)
- 问题:忘记释放
new
分配的内存。 - 解决方案:优先使用
std::vector
或智能指针:#include <memory> // 使用智能指针管理动态数组 std::unique_ptr<int[]> arr(new int[10]);
3. 避免C风格数组
- 建议:优先使用
std::array
或std::vector
替代原始数组,减少手动内存管理:// 推荐写法 std::array<int, 5> arr; // 固定大小 std::vector<int> vec; // 动态大小
通过合理使用数组和标准库容器,你能高效处理各种数据存储需求。建议优先使用std::vector
和std::array
,仅在性能敏感或与C代码交互时使用原始数组。