CC++ & Algorithm

C++ STL vector 动态数组 —— 可以自动变长的大箱子

中等34
语言版本:C++Python
概述:vector就像一个可以自动变大变小的箱子,你想放多少东西就放多少,不用一开始就定好大小。

C++ STL 里的 vector:会自动长大的“零食箱”

你有没有遇到过这种情况:去超市买零食,不知道要买多少包,于是拿了一个小袋子,结果越装越多,袋子都快撑破了。要是有一个“会自动变大的袋子”就好了!C++ 里的 vector 就是这样一个聪明的“箱子”——它会根据你放进去的东西数量,自动变大,装下所有数据。它和普通数组最大的区别是:普通数组的大小是固定的(比如 int a[10] 永远只能装 10 个数),而 vector 可以随时往里加东西,它会自己扩大容量,完全不让你操心内存够不够用。


1. 怎么得到一个“箱子”?

要使用 vector,第一步是告诉 C++:“嘿,我要用这个智能箱子!” 方法就是加上一句:

#include <vector>   // 这是 vector 的“使用说明书”

然后,你就可以用这样的格式创建一个箱子:

vector<你想装的东西的类型> 箱子的名字;

比如,你想装全班同学的考试成绩(都是整数),就这样写:

vector<int> scores;   // 一个空的整数箱子,专门用来装分数

这个 scores 一开始是空的,里面啥也没有,就像你刚买回来的空书包。

生活小例子:你有一个“自动变大”的收纳箱,一开始是扁扁的,放一本书它就撑大一点,放十本书它就变大很多,永远不用担心箱子太小。


2. 往箱子里放东西、拿东西

2.1 在末尾放一个元素:push_back

push_back 就像把一包零食从箱子顶部塞进去。每次塞进去,箱子大小自动 +1。

scores.push_back(95);   // 放进去第一个分数 95
scores.push_back(87);   // 再放进去 87
scores.push_back(92);   // 再放 92
// 现在 scores 里有 3 个分数:95, 87, 92

2.2 拿走最后一个元素:pop_back

如果你后悔了,想拿出最后放进去的那包零食,就用 pop_back

scores.pop_back();      // 拿走了最后一个 92,现在箱子里只剩 95 和 87

注意:这就像从一叠书上面拿走最上面那本,只能拿最后一本,不能拿中间的。

2.3 看看箱子里有几个元素:size

想数一数箱子里现在有几样东西?用 size() 就好啦,它返回一个整数。

cout << "目前箱子里有 " << scores.size() << " 个分数" << endl;
// 如果之前 push_back 了 3 个又 pop_back 了一个,结果就是 2

2.4 通过“下标”访问任意位置的元素:[ ]

你学过普通数组 a[0] 这种写法吧?vector 也支持同样的方括号下标,下标从 0 开始

cout << "第一个分数是:" << scores[0] << endl;   // 输出 95
cout << "第二个分数是:" << scores[1] << endl;   // 输出 87

就像你给箱子里的零食贴上了标签:第 0 号是薯片,第 1 号是巧克力……但记住,不要访问下标 >= size() 的位置,不然就“越界”了,程序会出问题(后面会讲)。

2.5 从头到尾遍历:用 for 循环

你可以像处理普通数组一样,用 for 循环把箱子里的东西都看一遍:

for (int i = 0; i < scores.size(); i++) {
    cout << scores[i] << " ";   // 依次输出每个分数
}

或者用 C++11 引入的“范围 for 循环”,更简单:

for (int score : scores) {
    cout << score << " ";
}

3. 还有更多实用操作

除了上面的,vector 还有很多方便的小工具,下面列出几个最常用的:

操作作用生活比喻
v.clear()清空所有元素,箱子变回空的把零食全部倒出来,箱子变回扁扁的
v.empty()判断箱子是否为空,空则返回 true摸一摸箱子,空的就告诉你是空的
v.front()返回第一个元素拿出最下面的那包薯片看看(但不拿出)
v.back()返回最后一个元素拿出最上面那包巧克力看看
v.insert(pos, val)在指定位置插入一个值(位置用迭代器)把一包饼干插在箱子的第 2 层中间
v.erase(pos)删除指定位置的元素从中间抽走一包零食

注意inserterase 的操作会移动元素,就像你从一叠书中间抽走一本,上面的书都往下掉一样,可能会让程序变慢,但不用担心,vector 会帮你处理好。

举一个使用 insert 的例子:

vector<int> numbers;
numbers.push_back(10);
numbers.push_back(20);
numbers.push_back(30);   // 现在是 10, 20, 30

// 想在 20 前面插入 15
numbers.insert(numbers.begin() + 1, 15);   // 变成 10, 15, 20, 30

这里的 numbers.begin() 是指向第一个元素的“指针”(叫迭代器),+1 就跳到第二个位置。


4. 新手最容易犯的三个错误

❌ 错误一:滥用 [ ] 访问不存在的元素

vector<int> v;   // 空的箱子
v[0] = 5;        // ❌ 危险!箱子是空的,没有第 0 个位置

正确做法:要么先用 push_back 放入元素,要么用 resize 设置大小后再用下标。

❌ 错误二:在循环中给 vector 不断增加元素导致“迭代器失效”

比如下面这个循环,想给 scores 每个成绩加 10 分,顺便再加一个新成绩,结果可能会出问题:

for (int i = 0; i < scores.size(); i++) {
    scores.push_back(scores[i] + 10);   // ❌ 每加一个 size 就变大,循环可能停不下来!
}

教训:不要在遍历 vector 的时候同时使用 push_backpop_back 改变大小,除非你很清楚后果。通常先记下原始长度再用。

❌ 错误三:忘记包含头文件

vector<int> v;   // ❌ 没有 #include <vector>,编译器不认识 vector

记得在文件最前面加上 #include <vector>


5. 完整示例:记录零花钱的出入账

下面这个程序模拟了一周零花钱的记录:你每天存一点、花一点,最后算算还剩多少钱。

#include <iostream>
#include <vector>       // 使用 vector 必须包含
using namespace std;

int main() {
    vector<int> money;  // 创建一个空箱子,用来记录每天的零花钱变化
    int total = 0;      // 初始总钱数为 0

    // 第1天:妈妈给 20 元
    money.push_back(20);
    // 第2天:买文具花掉 8 元(记作 -8)
    money.push_back(-8);
    // 第3天:帮邻居跑腿赚了 15 元
    money.push_back(15);
    // 第4天:买零食花掉 10 元
    money.push_back(-10);
    // 第5天:爸爸又给了 50 元
    money.push_back(50);

    cout << "这5天一共有 " << money.size() << " 笔记录" << endl;

    // 用循环计算总钱数
    for (int i = 0; i < money.size(); i++) {
        total = total + money[i];   // 把这天的变化加到 total 上
    }
    cout << "最后还剩 " << total << " 元" << endl;

    // 再模拟一天:捡到 5 元
    money.push_back(5);
    cout << "现在总共有 " << money.size() << " 笔记录,钱数为:";
    total = 0;
    for (int m : money) {   // 用范围 for 重新计算
        total = total + m;
    }
    cout << total << " 元" << endl;

    // 清空记录,准备下一周
    money.clear();
    cout << "清空后,箱子里有 " << money.size() << " 条记录" << endl;

    return 0;
}

运行结果(大概):

这5天一共有 5 笔记录
最后还剩 67 元
现在总共有 6 笔记录,钱数为:72 元
清空后,箱子里有 0 条记录

6. 为什么不用普通数组?

对比项普通数组 int a[100]vector 动态数组
大小固定(比如 100),多了装不下自动变大,塞多少装多少
尾部添加要自己记录下标,很麻烦push_back 一行搞定
获取长度要自己算(sizeof/int 或额外变量)size() 直接返回
灵活性不能直接插入、删除中间元素提供 inserterase

所以,只要你不确定数据到底有多少个,或者需要频繁往末尾加东西,就用 vector。它比普通数组更安全、更省心。


7. 还想学更多?

vector 是 C++ STL(标准模板库)中最基础的容器之一。有了 vector 的基础,你可以继续了解:

  • list(链表):可以快速在中间插入、删除,但访问元素比 vector 慢。
  • deque(双端队列):可以在头部和尾部快速添加/删除。
  • map(映射):像字典一样,通过“键”找到“值”,比如根据学号查成绩。

也可以学习 vector 的一些高级用法,比如二维 vector(装多个箱子的箱子)、用 sort 对 vector 排序等。

总之,vector 是你编程路上最忠诚的伙伴之一——它会自动长大,永远不让你失望。下次写程序需要存一堆东西时,记得先想到它哦!

例题精讲

1单选题

下列关于C++ STL vector的说法,错误的是?

Avector在内存中连续存储元素
Bvector的容量(capacity)总是等于其大小(size)
Cvector可以动态增长,不需要预先指定大小
D使用push_back可以在vector末尾添加元素
2判断题

对于一个空的vector对象,调用reserve(100)后,其size变为100。

3填空题
下列代码使用vector存储整数,并向其中添加1到10,然后遍历输出。请填写空缺部分。

#include <iostream>
#include <vector>
using namespace std;
int main() {
    vector<int> v;
    for (int i = 1; i <= 10; ++i) {
        ___(1)___;  // 将i添加到v末尾
    }
    for (int i = 0; i < v.___(2)___; ++i) {
        cout << v[i] << " ";
    }
    return 0;
}
4填空题
下列代码使用迭代器遍历vector<int>,并计算所有元素的和。请填写空缺部分。

#include <iostream>
#include <vector>
using namespace std;
int main() {
    vector<int> v = {2, 4, 6, 8, 10};
    int sum = 0;
    for (auto it = v.___(1)___; it != v.___(2)___; ++it) {
        sum += *it;
    }
    cout << "Sum = " << sum;
    return 0;
}
5单选题

已知vector<int> vec = {1, 2, 3, 4, 5},执行以下哪条语句后,vec的size()变为3?

Avec.resize(3);
Bvec.reserve(3);
Cvec.erase(vec.begin(), vec.end());
Dvec.pop_back(); vec.pop_back();