C++零基础到工程实战(4.3.1):数组与vector初识——连续内存与动态数组的本质解析

目录

一、本节学习内容概要图

二、前言

三、数组是什么

[3.1 数组的本质](#3.1 数组的本质)

[3.2 数组为什么访问快](#3.2 数组为什么访问快)

(1)访问值的本质

(2)访问地址

[3.3 数组变量的本质](#3.3 数组变量的本质)

(1)可以这样理解

(2)一个关键区别

(3)简单例子对比

四、C++中的数组分类

[4.1 栈区数组](#4.1 栈区数组)

(1)定义方式

(2)生命周期

(3)空间大小申请

(4)空间大小计算

(5)栈区特点

(6)优缺点

[4.2 堆区数组](#4.2 堆区数组)

(1)基本定义

(2)内存释放

(3)生命周期

(4)堆区特点

(5)优缺点

[4.3 vector(容器数组)](#4.3 vector(容器数组))

(1)头文件与命名空间

(2)vector语法本质

[(3)vector 的内存结构](#(3)vector 的内存结构)

[(4)vector 的核心优势](#(4)vector 的核心优势)

(5)常用操作示例

(6)迭代器访问

[4.4 三种数组的核心对比](#4.4 三种数组的核心对比)

[五、数组 vs vector 对比](#五、数组 vs vector 对比)

六、初始化是否为0

[6.1 栈区数组(局部数组)](#6.1 栈区数组(局部数组))

[6.2 堆区数组(动态分配数组)](#6.2 堆区数组(动态分配数组))

[6.3 总结](#6.3 总结)

七、本节总结

[7.1 数组核心](#7.1 数组核心)

[7.2 三种数组](#7.2 三种数组)

[7.3 一句话总结](#7.3 一句话总结)


一、本节学习内容概要图


二、前言

在前面的内容中,我们已经学习了:

  • 变量与基本类型
  • if / switch 条件控制
  • for / while 循环控制

但在实际开发中,仅仅会"控制流程"是不够的,我们还需要解决一个更核心的问题:

👉 数据如何存储?

例如:

  • 一批图片数据如何存储?
  • 一组路径点如何管理?
  • 多个传感器数据如何统一处理?
  • 大量计算结果如何高效访问?

这些问题,本质上都离不开一种结构:

数组(Array)与容器(Container)

在 C++ 中,我们最常用的就是:

  • 数组(Array)
  • vector(动态数组)

这一节,我们不只是学"怎么用",更重要的是:

👉 搞清楚它们的底层本质和内存结构


三、数组是什么

3.1 数组的本质

数组本质上是:

一段连续的内存空间

例如:

cpp 复制代码
int arr[5] = {1,2,3,4,5};

在内存中可以理解为:(int 4个字节)

cpp 复制代码
地址: 1000 1004 1008 1012 1016
数据: 1 2 3 4 5

👉 这里有几个非常关键的点:

  • 每个元素紧挨着存储(连续)
  • 每个元素类型相同(int)
  • 每个元素占用固定字节数(4字节)

3.2 数组为什么访问快

我们来看最常见的访问方式:

cpp 复制代码
arr[3];

(1)访问值的本质

这句代码本质上做的事情是:

首地址 + 下标 × 元素大小

等价写法:

cpp 复制代码
*(arr + 3);

👉 逐步拆解理解

  • arr → 数组首地址
  • arr + 3 → 第 4 个元素的地址
  • *(arr + 3) → 取该地址中的值

✅ 访问值总结

写法 含义
arr[3] 访问第4个元素的值
*(arr + 3) 访问第4个元素的值

(2)访问地址

如果我们想要"地址",就要这样写:

cpp 复制代码
&arr[3];

等价:

cpp 复制代码
arr + 3;

✅ 地址 vs 值对比(重点)

表达式 类型 含义
arr int* 首地址
arr + 3 int* 第4个元素地址
&arr[3] int* 第4个元素地址
*(arr + 3) int 第4个元素的值
arr[3] int 第4个元素的值

👉 为什么数组访问这么快?

因为 CPU 只需要:

  • 一次地址计算(偏移)
  • 一次内存读取

👉 不需要查表、不需要跳转

👉 时间复杂度:O(1)


✅ 一句话总结

arr + i 是地址,*(arr + i) 才是值


3.3 数组变量的本质

cpp 复制代码
int arr[5];

👉 这里最关键的一点是:

arr 本质上表示这段连续内存的"首地址"


(1)可以这样理解

cpp 复制代码
int* p = arr;

👉 完全等价成立

说明:

  • arr 可以当作一个指针使用
  • 指向数组的第一个元素

(2)一个关键区别

👉 arr 不是指针变量

而是:

数组名在大多数表达式中会"退化"为指针"


(3)简单例子对比

cpp 复制代码
int arr[5];
int* p = arr;
名称 本质
arr 数组名(固定地址)
p 指针变量(可以改变指向)

👉 关键区别

cpp 复制代码
p = p + 1; // ✅ 可以
arr = arr + 1; // ❌ 错误(数组名不能修改)

四、C++中的数组分类

在 C++ 中,数组并不只有一种形式。根据内存分配方式和管理方式的不同,通常可以分为三类:

  • 栈区数组(Stack Array)
  • 堆区数组(Heap Array)
  • vector(动态数组容器)

这三者的核心区别,本质上是:

内存在哪分配?谁来管理?是否可扩展?

4.1 栈区数组

(1)定义方式

cpp 复制代码
int arr[10];
string strs[10];
char str[10];

数组 = 类型 + 数量

👉 这种数组:

  • 定义在函数内部
  • 内存分配在栈区
  • 系统自动管理

(2)生命周期

cpp 复制代码
void func()
{
int arr[5];
}

👉 执行过程:

  • 进入函数 → 分配内存
  • 离开函数 → 自动释放

(3)空间大小申请

👉 通过 [] 控制:

cpp 复制代码
int arr[10];

👉 关键点(你图里的重点):

必须是编译期常量(constexpr)

❌ 不支持:

cpp 复制代码
int n = 10;
int arr[n]; // ❌ 标准C++不支持(GCC扩展除外)

(4)空间大小计算

cpp 复制代码
sizeof(arr);

👉 作用:

返回整个数组占用的字节数

例如:

cpp 复制代码
int arr[10];
cout << sizeof(arr); // 40

(5)栈区特点

  • 内存从高地址向低地址增长
  • 分配速度非常快(几乎是指令级)
  • 不需要程序员管理

(6)优缺点

✅ 优点:

  • 访问速度快
  • 自动释放(安全)
  • 使用简单

❌ 缺点:

  • 空间有限(容易栈溢出)
  • 大小必须固定

4.2 堆区数组

(1)基本定义

cpp 复制代码
int* arr = new int[5];

或者:

cpp 复制代码
auto arr3 = new char[4];

👉 这种数组:

  • 分配在堆区
  • 由程序员手动管理

(2)内存释放

cpp 复制代码
delete[] arr2;
delete[] arr3;

👉 如果不释放:

❌ 会发生内存泄漏


(3)生命周期

  • 不受作用域限制
  • 只要不 delete,就一直存在

(4)堆区特点

  • 内存从低地址向高地址增长
  • 空间较大
  • 分配速度比栈慢(涉及系统管理)

(5)优缺点

✅ 优点:

  • 支持动态分配(运行时决定大小)
  • 生命周期灵活

❌ 缺点:

  • 需要手动释放
  • 容易内存泄漏
  • 使用复杂

4.3 vector(容器数组)

(1)头文件与命名空间

cpp 复制代码
#include <vector>

std::vector<int> v;

👉 vector 是:

C++ STL 提供的动态数组容器

可以理解为:

👉 "自动管理的堆数组 + 更安全的接口"

(2)vector语法本质

cpp 复制代码
vector<元素类型> 变量名(初始数量);

例如:

cpp 复制代码
vector<int> v(10);

(3)vector 的内存结构

vector 的结构其实是"栈 + 堆"的结合:

栈区:

v(内部包含:指针 + size + capacity)

堆区:

实际存储的数据(连续空间)

👉 说明:

  • v 这个变量本身在栈上
  • 数据在堆上

(4)vector 的核心优势

相比普通数组:

✅ 自动扩容(不用关心大小)

✅ 自动释放(避免内存泄漏)

✅ 提供丰富操作接口

(5)常用操作示例

cpp 复制代码
v.push_back(10); // 末尾插入
v.erase(v.begin()); // 删除第一个元素

(6)迭代器访问

cpp 复制代码
for (auto it = v.begin(); it != v.end(); ++it)
{
cout << *it;
}

4.4 三种数组的核心对比

特性 栈区数组 堆区数组 vector
内存位置 栈 + 堆
生命周期 自动 手动 自动
大小 固定 可变 可变
安全性
使用难度 简单 较复杂 简单
是否推荐 小数据 特殊场景 ⭐工程首选

五、数组 vs vector 对比

特性 数组 vector
内存 连续 连续
大小 固定 可变
管理 手动 自动
安全性

六、初始化是否为0

6.1 栈区数组(局部数组)

cpp 复制代码
int arr[5]; // 局部变量
  • 特点:存储在栈上,通常是函数内部定义的局部变量。

  • 默认值:不初始化的话,内容是随机的(垃圾值),不会自动变成 0。

  • 初始化为 0 的方法

    cpp 复制代码
    int arr[5] = {}; // 所有元素自动初始化为 0
    int arr[5] = {0}; // 同样效果

6.2 堆区数组(动态分配数组)

cpp 复制代码
int* arr = new int[5]; // 动态分配
int* arr2 = new int[5]{}; // 带括号的动态分配
  • 特点存储在堆上,通过 newmalloc 分配。
  • 默认值
    • new int[5] → 不初始化,内容是随机的(垃圾值)
    • new int[5]{} → 初始化为 0

6.3 总结

类型 初始化方式 默认值
栈区数组 int a[5]; 随机值
栈区数组 int a[5] = {}; 0
堆区数组 new int[5]; 随机值
堆区数组 new int[5]{}; 0

简单记忆口诀:

局部不赋值 → 垃圾值;全局或静态 → 自动 0;new() → 可以选择初始化 0


七、本节总结

7.1 数组核心

  • 连续内存
  • O(1)访问
  • arr 是首地址

7.2 三种数组

类型 特点
栈数组 自动释放
堆数组 手动管理
vector 自动+动态

7.3 一句话总结

👉 数组 = 性能极致但不灵活

👉 vector = 工程首选容器

相关推荐
脱氧核糖核酸__2 小时前
LeetCode热题100——240.搜索二维矩阵 II(题目+题解+答案)
c++·算法·leetcode·矩阵
极客智造2 小时前
C++ 类模板完全深度指南:泛型编程、特化、分离编译与工程实践
c++
TU^2 小时前
C++11(二)
c++·算法
EverestVIP2 小时前
C++成员指针在库设计中的实际案例
c++
落羽的落羽2 小时前
【Linux系统】深入线程:多线程的互斥与同步原理,封装实现两种生产者消费者模型
java·linux·运维·服务器·c++·人工智能·python
小则又沐风a2 小时前
STL库(vector)逐步分析vector( 包含常用的接口的使用讲解)
开发语言·c++
故事和你9112 小时前
洛谷-数据结构1-1-线性表1
开发语言·数据结构·c++·算法·leetcode·动态规划·图论
脱氧核糖核酸__12 小时前
LeetCode热题100——53.最大子数组和(题解+答案+要点)
数据结构·c++·算法·leetcode
脱氧核糖核酸__12 小时前
LeetCode 热题100——42.接雨水(题目+题解+答案)
数据结构·c++·算法·leetcode