目录
[1.1. 核心价值](#1.1. 核心价值)
[1.2. 主要类型对比](#1.2. 主要类型对比)
[2.1 什么是数组](#2.1 什么是数组)
[2.2 数组的声明和初始化](#2.2 数组的声明和初始化)
[2.2.1 声明数组](#2.2.1 声明数组)
[2.2.2 初始化数组](#2.2.2 初始化数组)
[2.3 访问数组元素](#2.3 访问数组元素)
[3.1 一维数组的遍历](#3.1 一维数组的遍历)
[3.2 一维数组的常见操作](#3.2 一维数组的常见操作)
[4.1 二维数组的概念](#4.1 二维数组的概念)
[4.2 二维数组的声明和初始化](#4.2 二维数组的声明和初始化)
[4.3 二维数组的遍历](#4.3 二维数组的遍历)
[4.4 二维数组的应用场景](#4.4 二维数组的应用场景)
[5.1 三维数组的声明和初始化](#5.1 三维数组的声明和初始化)
[5.2 多维数组的遍历](#5.2 多维数组的遍历)
[6.1 数组作为函数参数](#6.1 数组作为函数参数)
[6.2 函数返回数组](#6.2 函数返回数组)
[7.1 优点](#7.1 优点)
[7.2 缺点](#7.2 缺点)
在 C++ 编程中,数组是一种非常基础且重要的数据结构。它允许存储和管理一组相同类型的数据元素。无论是处理简单的数据集合,还是构建复杂的算法和程序,数组都扮演着不可或缺的角色。
一、数组概述:数据存储的基石
1.1. 核心价值
-
连续内存空间存储相同类型元素
-
支持随机访问(时间复杂度O(1))
-
编译期确定大小(静态数组)或运行时动态分配(动态数组)
-
内存效率极高,无额外管理开销
1.2. 主要类型对比
特性 | 内置数组 | std::array(C++11) | std::vector |
---|---|---|---|
大小确定 | 编译时 | 编译时 | 运行时 |
内存管理 | 自动/手动 | 自动 | 自动 |
边界检查 | 无 | at()方法提供 | at()方法提供 |
迭代器支持 | 原生指针 | 标准迭代器 | 标准迭代器 |
传递效率 | 退化指针 | 值/引用传递 | 引用传递 |
二、数组的基本概念
2.1 什么是数组
数组是一种由相同类型的元素组成的集合,这些元素在内存中是连续存储的。每个元素都可以通过一个唯一的索引来访问,索引通常是一个整数,从 0 开始计数。例如,一个包含 5 个整数的数组可以表示为int arr[5]
,其中arr
是数组的名称,5
是数组的大小。
2.2 数组的声明和初始化
2.2.1 声明数组
在 C++ 中,声明数组需要指定数组的类型和大小。以下是一些声明数组的示例:
cpp
// 声明一个包含5个整数的数组
int arr1[5];
// 声明一个包含10个字符的数组
char arr2[10];
// 声明一个包含3个双精度浮点数的数组
double arr3[3];
需要注意的是,数组的大小必须是一个常量表达式,即在编译时就可以确定的值。
2.2.2 初始化数组
数组可以在声明时进行初始化,也可以在声明后再进行赋值。以下是几种常见的初始化方式:
①逐个元素初始化
cpp
int arr[5] = {1, 2, 3, 4, 5};
声明了一个包含 5 个整数的数组,并使用大括号{}
对每个元素进行了初始化。数组的第一个元素是1
,第二个元素是2
,以此类推。
② 部分元素初始化
cpp
int arr[5] = {1, 2};
在这种情况下,数组的前两个元素被初始化为1
和2
,其余元素会被自动初始化为 0。
③省略数组大小
cpp
int arr[] = {1, 2, 3, 4, 5};
当使用大括号初始化数组时,可以省略数组的大小,编译器会根据初始化列表中的元素个数自动确定数组的大小。
④默认初始化
cpp
int arr[5];
如果只声明数组而没有进行初始化,数组中的元素将是未定义的。对于全局数组和静态数组,元素会被自动初始化为 0;对于局部数组,元素的值是不确定的。
2.3 访问数组元素
数组元素可以通过索引来访问。索引是一个整数,从 0 开始计数,最大索引为数组大小减 1。以下是一个访问数组元素的示例:
cpp
#include <iostream>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
std::cout << "数组的第一个元素是: " << arr[0] << std::endl;
std::cout << "数组的第三个元素是: " << arr[2] << std::endl;
return 0;
}

通过索引0
和2
分别访问了数组的第一个和第三个元素,并将其输出。
三、一维数组
3.1 一维数组的遍历
遍历数组意味着依次访问数组中的每个元素。常见的遍历方式有使用for
循环和while
循环。
①使用for
循环遍历
cpp
#include <iostream>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
return 0;
}

使用for
循环从索引 0 开始,依次访问数组的每个元素,并将其输出。
②使用while
循环遍历
cpp
#include <iostream>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int i = 0;
while (i < 5) {
std::cout << arr[i] << " ";
i++;
}
std::cout << std::endl;
return 0;
}

使用while
循环实现了同样的遍历功能。
3.2 一维数组的常见操作
**①查找元素:**查找数组中是否存在某个特定的元素是一个常见的操作。以下是一个简单的查找函数:
cpp
#include <iostream>
bool findElement(int arr[], int size, int target) {
for (int i = 0; i < size; i++) {
if (arr[i] == target) {
return true;
}
}
return false;
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int target = 3;
if (findElement(arr, 5, target)) {
std::cout << "元素 " << target << " 存在于数组中。" << std::endl;
} else {
std::cout << "元素 " << target << " 不存在于数组中。" << std::endl;
}
return 0;
}

定义了一个findElement
函数,用于查找数组中是否存在指定的元素。
②求数组元素的和
计算数组中所有元素的和也是一个常见的操作。以下是一个求和函数:
cpp
#include <iostream>
int sumArray(int arr[], int size) {
int sum = 0;
for (int i = 0; i < size; i++) {
sum += arr[i];
}
return sum;
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int total = sumArray(arr, 5);
std::cout << "数组元素的和是: " << total << std::endl;
return 0;
}

定义了一个sumArray
函数,用于计算数组中所有元素的和。
③数组排序
对数组进行排序可以使数组元素按照一定的顺序排列。C++ 标准库提供了std::sort
函数来实现排序功能。
cpp
#include <iostream>
#include <algorithm>
int main() {
int arr[5] = {5, 3, 1, 4, 2};
std::sort(arr, arr + 5);
for (int i = 0; i < 5; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
return 0;
}

使用std::sort
函数对数组进行了升序排序,并将排序后的数组输出。
四、二维数组
4.1 二维数组的概念
二维数组可以看作是一个表格,由行和列组成。它可以用来表示矩阵、棋盘等数据结构。在 C++ 中,二维数组的声明和初始化方式与一维数组类似。
4.2 二维数组的声明和初始化
① 声明二维数组
cpp
// 声明一个3行4列的整数二维数组
int arr[3][4];
声明了一个包含 3 行 4 列的整数二维数组。
②初始化二维数组
cpp
// 初始化一个3行4列的整数二维数组
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
使用嵌套的大括号对二维数组进行了初始化。每一行的元素用一个小括号括起来,不同行之间用逗号分隔。
4.3 二维数组的遍历
二维数组的遍历需要使用嵌套循环。外层循环控制行,内层循环控制列。
cpp
#include <iostream>
int main() {
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
std::cout << arr[i][j] << " ";
}
std::cout << std::endl;
}
return 0;
}

4.4 二维数组的应用场景
二维数组在很多领域都有广泛的应用,例如图像处理、游戏开发等。在图像处理中,二维数组可以用来表示图像的像素矩阵;在游戏开发中,二维数组可以用来表示游戏地图。
五、多维数组
除了一维数组和二维数组,C++ 还支持多维数组。多维数组的概念和操作与二维数组类似,只是维度更多。例如,三维数组可以看作是由多个二维数组组成的。
5.1 三维数组的声明和初始化
cpp
// 声明一个2个平面,每个平面3行4列的三维整数数组
int arr[2][3][4];
// 初始化三维数组
int arr[2][3][4] = {
{
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
},
{
{13, 14, 15, 16},
{17, 18, 19, 20},
{21, 22, 23, 24}
}
};
5.2 多维数组的遍历
多维数组的遍历需要使用多层嵌套循环。例如,遍历三维数组需要使用三层嵌套循环。
cpp
#include <iostream>
int main() {
int arr[2][3][4] = {
{
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
},
{
{13, 14, 15, 16},
{17, 18, 19, 20},
{21, 22, 23, 24}
}
};
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 4; k++) {
std::cout << arr[i][j][k] << " ";
}
std::cout << std::endl;
}
std::cout << std::endl;
}
return 0;
}

用三层嵌套的for
循环遍历了三维数组的每个元素,并将其输出。
六、数组与函数
数组可以作为参数传递给函数,也可以作为函数的返回值。
6.1 数组作为函数参数
当数组作为函数参数时,实际上传递的是数组的首地址。以下是一个示例:
cpp
#include <iostream>
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
printArray(arr, 5);
return 0;
}

定义了一个printArray
函数,用于打印数组的元素。函数的第一个参数是一个整数数组,第二个参数是数组的大小。
6.2 函数返回数组
在 C++ 中,函数不能直接返回数组,但可以返回指向数组的指针。以下是一个示例:
cpp
#include <iostream>
int* createArray() {
int* arr = new int[5];
for (int i = 0; i < 5; i++) {
arr[i] = i + 1;
}
return arr;
}
int main() {
int* arr = createArray();
for (int i = 0; i < 5; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
delete[] arr; // 释放动态分配的内存
return 0;
}

定义了一个createArray
函数,用于创建一个包含 5 个整数的数组,并返回该数组的指针。在main
函数中,调用createArray
函数获取数组指针,并打印数组的元素。最后,使用delete[]
释放了动态分配的内存,以避免内存泄漏。
七、数组的优缺点
7.1 优点
- 高效的随机访问:数组元素在内存中是连续存储的,因此可以通过索引快速访问任意元素,时间复杂度为 O (1)。
- 简单易用:数组的声明和操作都比较简单,容易理解和掌握。
7.2 缺点
- 大小固定 :数组的大小在声明时就已经确定,不能在运行时动态调整。如果需要动态调整大小,可以使用
std::vector
。 - 插入和删除操作效率低:在数组中插入或删除元素需要移动大量的元素,时间复杂度为 O (n)。
八、内存机制深度剖析
①内存布局示例
cpp
int arr[5] = {10,20,30,40,50};
内存结构:
cpp
地址 | 值
0x1000 | 10 // arr[0]
0x1004 | 20 // arr[1]
0x1008 | 30 // arr[2]
0x100C | 40 // arr[3]
0x1010 | 50 // arr[4]
②指针与数组关系
cpp
int* ptr = arr; // 等价于 &arr[0]
ptr += 3; // 指向arr[3]
*(ptr-1) = 100; // 修改arr[2]
// 数组名不可修改
arr = ptr; // 编译错误
③数组退化的陷阱
cpp
void process(int arr[]) { // 实际退化为int*
cout << sizeof(arr); // 输出指针大小(如8字节)
}
int main() {
int arr[5] = {0};
cout << sizeof(arr); // 输出20(5*4字节)
process(arr);
}
九、高级操作技巧
①数组引用(C++特性)
cpp
// 保持数组类型信息
template<size_t N>
void print_array(int (&arr)[N]) { // 精确匹配数组大小
for(int i=0; i<N; ++i)
cout << arr[i] << " ";
}
int arr[] = {1,2,3};
print_array(arr); // 正确传递数组引用
②动态内存管理
cpp
// 创建动态数组
int* dynArr = new int[10];
// 初始化动态数组
int* dynArr2 = new int[5]{1,2,3,4,5}; // C++11
// 释放内存
delete[] dynArr; // 必须配对使用
③数组与算法结合
cpp
#include <algorithm>
#include <numeric>
int arr[] = {3,1,4,2,5};
// 排序
std::sort(std::begin(arr), std::end(arr));
// 累加求和
int sum = std::accumulate(arr, arr+5, 0);
// 查找元素
auto it = std::find(arr, arr+5, 4);
if(it != arr+5) cout << "Found at index: " << it - arr;
十、性能优化实战
①缓存友好访问模式
cpp
// 优先行序访问(内存连续)
int matrix[1000][1000];
for(int i=0; i<1000; ++i) // 外层行循环
for(int j=0; j<1000; ++j) // 内层列循环
matrix[i][j] = i+j;
// 避免跳行访问
for(int j=0; j<1000; ++j) // 错误示范
for(int i=0; i<1000; ++i)
matrix[i][j] = i+j; // 缓存命中率低
②SIMD矢量化
cpp
// 使用编译器指令优化
#pragma omp simd
for(int i=0; i<N; ++i) {
a[i] = b[i] + c[i];
}
// 手动使用AVX指令
#include <immintrin.h>
void add_arrays(float* a, float* b, float* c, int n) {
for(int i=0; i<n; i+=8) {
__m256 va = _mm256_load_ps(&a[i]);
__m256 vb = _mm256_load_ps(&b[i]);
__m256 vc = _mm256_add_ps(va, vb);
_mm256_store_ps(&c[i], vc);
}
}
③循环展开优化
cpp
// 手动展开循环
for(int i=0; i<1024; i+=4) {
arr[i] = i;
arr[i+1] = i+1;
arr[i+2] = i+2;
arr[i+3] = i+3;
}
十一、现代C++特性应用
①结构化绑定(C++17)
cpp
std::array<int,3> get_point() { return {10,20,30}; }
auto [x,y,z] = get_point(); // 解包数组元素
cout << "X: " << x << ", Y: " << y;
②constexpr数组(C++11)
cpp
constexpr int fib[] = {0,1,1,2,3,5,8,13};
static_assert(fib[7] == 13, "Fibonacci error");
③范围视图(C++20)
cpp
#include <ranges>
int arr[] = {1,2,3,4,5,6};
// 过滤偶数并反转
for(int v : arr | views::filter([](int x){return x%2==0;})
| views::reverse)
{
cout << v << " "; // 输出6 4 2
}
十二、常见问题解决方案
① 数组越界预防
cpp
template<typename T, size_t N>
class SafeArray {
T data[N];
public:
T& operator[](size_t index) {
if(index >= N) throw std::out_of_range("Index overflow");
return data[index];
}
};
②动态数组内存管理
cpp
// 使用智能指针(C++11)
auto dynArr = std::make_unique<int[]>(100); // 自动释放内存
dynArr[50] = 123;
// 容器封装
std::vector<int> safeDynArr(100); // 推荐替代方案
③深拷贝实现
cpp
// 内置数组深拷贝
int src[5] = {1,2,3,4,5};
int dest[5];
std::copy(std::begin(src), std::end(src), dest);
// std::array自动深拷贝
std::array<int,5> a = {1,2,3,4,5};
auto b = a; // 完全独立拷贝
十三、总结
数组是 C++ 编程中非常基础和重要的数据结构。数组具有高效的随机访问能力,但也存在大小固定和插入删除操作效率低的缺点。在实际编程中,需要根据具体的需求选择合适的数据结构。
希望本文能够帮助初学者全面掌握数组的使用,为进一步学习 C++ 编程打下坚实的基础。
十四、参考资料
- **《C++ Primer(第 5 版)》**这本书是 C++ 领域的经典之作,对 C++ 的基础语法和高级特性都有深入讲解。
- **《Effective C++(第 3 版)》**书中包含了很多 C++ 编程的实用建议和最佳实践。
- 《C++ Templates: The Complete Guide(第 2 版)》 该书聚焦于 C++ 模板编程,而
using
声明在模板编程中有着重要应用,如定义模板类型别名等。 - C++ 官方标准文档:C++ 标准文档是最权威的参考资料,可以查阅最新的 C++ 标准(如 C++11、C++14、C++17、C++20 等)文档。例如,ISO/IEC 14882:2020 是 C++20 标准的文档,可从相关渠道获取其详细内容。
- cppreference.com:这是一个非常全面的 C++ 在线参考网站,提供了详细的 C++ 语言和标准库文档。
- LearnCpp.com:该网站提供了系统的 C++ 教程,配有丰富的示例代码和清晰的解释,适合初学者学习和理解相关知识。