数组是存放在<连续内存空间>上<类型相同> 的数据集合
支持随机访问 O(1)
数组的核心优势就是其随机访问能力,它允许我们通过索引直接、迅速地访问数组中的任意元素。
数组寻址公式:
address(arri) = base_address(arr) + i * sizeof_element
假如我们有一个长度为5的int数组,其基地址是0x0053fbd4,要访问第五个元素,即arr4,那么根据寻址公式就可以计算出地址:
address_arr4 = 0x0053fbd4 + (4 * 4) = 0x0053fbe4
遍历数组方式:
for (i = 0; i < sizeof(arr) / sizeof(arr0 ); i++){
printf("%d", arri);
}
使用C++的话,要注意vector 和 array的区别,vector的底层实现是array,严格来讲vector是容器,不是数组。
C++STL数组:
std::vector: 动态数组,提供快速随机访问。
std::array: 固定大小的数组,具有静态分配的内存。
std::deque
●基于动态数组:std::deque 是一个基于动态数组的序列容器,可以高效地从两端添加或删除元素。
●允许序列操作:可以快速地在队列的前端和后端添加或删除元素。
●时间复杂度:提供常数时间复杂度 (O(1)) 的前端和后端插入和删除操作。中间插入或删除操作可能需要 O(n) 时间。
- deque(分段连续存储)
核心结构:底层:一个指针数组(中控 map),每个指针指向一个固定大小的连续缓冲区。
push_front/push_back 只需在当前块的前/后填充,块满了就分配新块并在中控 map 中加一个指针。随机访问:先算目标在第几个块(偏移量 / 块大小),再算块内位置(偏移量 % 块大小),两次除法就定位了,所以是 O(1)。中间插入需要移动元素,O(n)。
一、std::vector 核心特性详解
- 它是封装动态数组的顺序容器,定义于头文件 :
- 内存管理:元素连续存储,内存自动按需扩容(1.5/2倍)将旧元素全部拷贝/移动过去后释放旧内存,不需要手动管理堆内存。
- 在std::vector扩容时,如果元素类型提供了移动构造函数且该函数被声明为noexcept,则会优先使用移动构造函数。否则,std::vector会使用拷贝构造函数进行元素的复制。
- 时间复杂度:随机访问:O(1),可以像普通数组一样用下标直接访问任意位置元素,尾部插入/删除:均摊O(1),性能极高,中间位置插入/删除:O(n),需要移动后续所有元素
- 实用优化技巧:如果提前知道元素总数量,可以调用reserve()预分配内存,完全避免运行时扩容的性能开销;不需要的预留空间可以通过shrink_to_fit()释放还给系统。
- shrink_to_fit() 会向编译器发起一个非强制请求,尝试****重新分配一块 刚好能容纳当前所有元素的新内存,将数据移动/拷贝过去后释放旧的冗余内存。
- 适用场景:需要频繁随机访问、元素数量会动态变化、主要在尾部增删元素的场景,比如算法题中的临时数据存储、动态采集的传感器数据缓存等。
二、std::array 核心特性详解
它是C++11引入 的固定大小静态数组容器,定义于头文件 :
- 内存管理:大小在编译期就必须指定,内存是栈上静态分配的,完全没有动态内存分配的开销,性能和原生C数组几乎一致。
- 核心优势:比原生C数组更安全,自带size()、at()(带下标越界检查)、fill()等便捷接口,同时保留了原生数组连续内存、可直接转成裸指针传给C接口的特性。
- 时间复杂度:所有操作都是O(1),随机访问效率拉满。
- 适用场景:元素数量编译期就确定、追求极致性能的场景,比如存储高速轴承故障诊断里固定长度的采样窗口数据、RGB三原色值、固定维度的小矩阵等。
三、两者核心差异对比表
如果数据量是动态变化的优先选vector,如果是固定长度的小数据集优先选array,能获得更好的性能表现。
C++程序的内存主要分为栈区、堆区、全局/静态区、常量区和代码区。栈区由编译器自动管理,用于存储局部变量、函数参数等,其分配和释放遵循函数调用栈的规则,空间有限。堆区则由程序员通过new或malloc等操作动态申请和释放,空间相对较大。
对于数组而言,其存储位置取决于定义方式:
- ● 在函数内部定义的静态数组(例如 int arr10;),其内存由编译器在栈上自动分配,函数返回时自动释放。
- ● 用new操作符动态创建的数组(例如 int* arr = new int10;),其内存是在堆区分配的,需要程序员手动使用delete\[\]释放,否则可能导致内存泄漏。
- ● 全局数组或静态局部数组(使用static关键字)则存储在全局/静态区。
内存相关补充:
- 栈区的空间限制:Windows下默认栈空间通常只有1MB~8MB,Linux下一般是8MB左右,如果在函数内定义int arr1000000这种超大局部数组,很容易直接触发栈溢出崩溃,这也是工程里大数组必须用动态分配的核心原因。
- 全局/静态区的细分:它还可以拆成两个子段:已初始化的全局/静态变量存在Data段,未初始化的会存在BSS段,系统会自动把BSS段的内存初始化为0,不需要程序员手动赋初值。
- 常量区的只读属性:字符串字面量、const全局常量都存放在这里,操作系统会给这页内存加只读保护,尝试修改会直接触发段错误,比如写char* p = "hello"; p0 = 'H'; 程序运行时会直接崩溃。
- new和malloc的小区别:new不仅会在堆上分配内存 ,还会自动调用对象的构造函数 ,对应的delete\[\]会调用数组里所有元素的析构函数,这是C++面向对象特性在内存管理上的体现。
-----随笔记录与学习,部分内容参考自网络,有疑问可以留言。>