Lesson 0005 · string 与 vector

① 程序设计基础 — 第 4 课

上节课:三种循环 + 数组,能批量处理数据了。但原生数组有个痛处 —— 大小写死、越界不报错、还得自己记长度。
这节课:认识 C++ 提供的两个更好用的工具 —— string(字符串)和 vector(动态数组)。它们的共同点是:长度自己会涨,还知道自己是多长。
三句话速览

1. string — 字符串不是字符数组

原生 C 风格字符串 char s[100] 容易越界、操作麻烦。C++ 的 string 是正道:

#include <string>       // 别忘了!

string s = "hello";
string t = " world";
string u = s + t;        // 拼接!cout << u  → "hello world"

cout << s.length();     // 长度:5
cout << s[0];           // 单个字符:'h'(索引从 0 开始)
cout << s.back();       // 最后一个字符:'o'

常用操作一表通

操作代码说明
长度s.length()字符个数
拼接s + t 或 s += t直接加号
比较s == t内容相等就 true
取子串s.substr(2, 3)从下标 2 开始取 3 个
查找s.find("lo")返回下标,找不到返 string::npos
清空s.clear()变回空串
是否空s.empty()空串返回 true
末尾加s.push_back('!')加一个字符

遍历 string

string s = "hello";
for (int i = 0; i < s.length(); i++) {
    cout << s[i] << " ";     // h e l l o
}

// C++ 专属:range-based for(更简洁)
for (char c : s) {
    cout << c << " ";        // h e l l o
}
注意:cin >> s 读到空格就停。要读一行用 getline(cin, s)。

2. vector — 大小可变的数组

原生数组 int a[100] 的三个问题 vector 全解决了:

#include <vector>

vector<int> v;               // 空的
v.push_back(3);             // 往后加:v = {3}
v.push_back(7);             // v = {3, 7}
v.push_back(1);             // v = {3, 7, 1}

cout << v.size();           // 3(动态大小!)
cout << v[1];               // 7(跟数组一样用)
v.pop_back();                // 删掉最后一个:v = {3, 7}

vector 常用操作

操作代码
声明vector<int> v;
声明+大小vector<int> v(5);(5 个 0)
声明+初始值vector<int> v(5, -1);(5 个 -1)
末尾加v.push_back(x);
末尾删v.pop_back();
大小v.size()
清空v.clear()
最后元素v.back()
首元素v.front()

遍历 vector

vector<int> v = {3, 1, 4, 1, 5};

for (int i = 0; i < v.size(); i++) {
    cout << v[i] << " ";     // 经典方式
}

for (int x : v) {               // 更简洁(range-based for)
    cout << x << " ";
}

3. 数组 vs vector 速查

原生数组vector
大小写死 a[100]动态增长
知道大小?需要手动记.size()
越界不报错不报错
何时用大小确定不变绝大多数情况
建议:以后默认用 vector,只有明确知道大小且固定时用数组。vector 不会浪费什么性能,省心太多。

4. 实战示例

读入 n 个数,逆序输出

int n;
cin >> n;
vector<int> v(n);
for (int i = 0; i < n; i++) cin >> v[i];
for (int i = n - 1; i >= 0; i--) cout << v[i] << " ";

判断回文字符串

string s;
cin >> s;
bool ok = true;
for (int i = 0; i < s.length() / 2; i++) {
    if (s[i] != s[s.length() - 1 - i]) {
        ok = false;
        break;
    }
}
cout << (ok ? "是回文" : "不是回文") << endl;

5. 坑点清单

6. 小测

Q1. string s = "abc"; s.push_back('d'); 后 s 是什么?
Q2. vector<int> v; v.push_back(1); v.pop_back(); cout << v.size(); 输出?
Q3. cin >> s 输入 "hello world",s 是什么?

7. 动手练习

题目:统计字符

读入一个字符串(可能含空格,用 getline),统计其中大写字母、小写字母和数字各有多少个。

示例:输入 Hello123 → 输出:大写 1,小写 4,数字 3

提示:isupper(c), islower(c), isdigit(c) 需要 #include <cctype>。
挑战题(选做):去重

读入 n 和 n 个整数,输出去掉重复数字后的结果(保持首次出现的顺序)。

示例:6 → 3 1 4 1 5 4 → 输出 3 1 4 5

卡住时的自查顺序

  1. 整行读进来了吗?先单独 cout << s; 看看空格后面的内容在不在。
  2. 遍历用的是 for (char c : s) 还是下标?两种都可以,但别再写 i <= s.length() 这种越界条件。
  3. 三个计数器都初始化为 0 了吗?只写 int up; 的话,加出来的数是垃圾值。
  4. 示例对不上时,换个只有字母的输入再试,缩小到是哪一类字符没数对。

本课小结

推荐阅读

下一课预告
0006 嵌套循环 —— 一个循环处理一维的东西,那两个循环套起来,就能处理表格:行列、矩阵、九九乘法表、图形的打印。循环里再套循环,是「多个维度」的入门。
以后几乎所有题都在和 string、vector 打交道,把它们当数组用就对了。哪一句还不明白 —— 比如「length() 和 size() 有区别吗」「getline 为什么会读到空行」「vector 和数组到底差在哪」—— 直接问。

Lesson 0005 · 课程体系总览:curriculum-overview.html

上一课:循环与数组 · Mission · STL 速查表

书 C++ Primer 第 3 章 · 在线 cppreference vector