课程 · C++ 速查 · STL 专题 · 第 0 块

C++ 上路先知道这几句

认出每份代码前两行都在干什么,知道 auto 是什么,知道读一行和读一个词的区别,以及 size() 为什么不能直接和 int 比。

第 0 块(共 12 块已写好)8 条动作3 处陷阱2 道例题3 道练习5 份可运行代码

读这一页之前,先知道读完要能做什么

  • 说清#include <bits/stdc++.h> 和 using namespace std; 这两行是干嘛的
  • 写出来cin >> x 读一个词、getline 读一整行,且知道 cin >> 之后直接 getline 会读到空行
  • 知道auto 是「类型编译器自己推」,以及 size() 是无符号数、0 − 1 会绕回
  • 用过 freopen 把输入输出重定向到文件(并知道交评测前必须删掉)

这是什么

7 段

一句话:每个新文件前两行都是一样的,后面所有板块都默认你已经写了它们。

★ #include <bits/stdc++.h>:把 C++ 标准库一次全部带进来 —— 之后 vector、string、cout 都能直接用,不用一个个 include。

★ using namespace std;:标准库里的名字默认都带 std:: 前缀(std::cout)。有了这行可以直接写 cout —— 本书从头到尾都这么写。

★ auto:让编译器自己推类型。auto x = 1; 就是 int x = 1;;for (auto &[k, v] : m) 里两个名字的类型也是它推的。看不懂可以先当成「那种类型」。

★ s.size()、v.size() 返回的是无符号数 —— 装不下负数,0 − 1 会绕回一个天文数字。所以和 int 比大小时一律写 (int)v.size()。

★ 读入分两种:cin >> s 读一个词(空格、换行都截断);getline(cin, s) 读一整行。⚠ cin >> n 之后直接 getline 会把残留的换行当成「一行」读走 —— 见陷阱 ①。

★ 本地调试想让程序「从文件读、往文件写」,不用改任何读写代码,在 main 开头加两行 freopen 重定向就行 —— 见陷阱 ③ 和练习。

完整动作表

8 条

复杂度写的是这个动作自己的代价,不是整个程序的。「这一栏最容易错的地方」那一列是重点。

写法干什么代价
#include <bits/stdc++.h>把 C++ 标准库一次全部带进来
★ 本书一律这样写,不用记一个个 #include
—
using namespace std;之后写 cout、vector 不用再带 std:: 前缀—
auto x = ...;类型由编译器自己推
★ 第一次看到 auto 当「那种类型」读就行
—
cin >> s;读一个词:空格、换行都在那儿截断
★ 读数、读没有空格的名字用它
—
getline(cin, s);读一整行:含空格,换行才截断
⚠ 和 cin >> 混用时注意残留的换行(陷阱 ①)
—
s.size();字符个数(string)/ 元素个数(vector)
⚠ 返回无符号数:和 int 比写 (int)s.size()
O(1)
freopen("in.txt", "r", stdin);把「读」重定向到文件 in.txt
★ 之后 cin/scanf 都从它读,代码不用改
—
freopen("out.txt", "w", stdout);把「写」重定向到文件 out.txt
★ 之后 cout/printf 都写进它
—

⭐ 这一节最容易错的地方(3 处)

这一节排在例题前面是故意的:读到一个坑,再去看正确的例子,记得住。 下面每一条都是「编译得过去、答案可能是错的」那种 —— 它们才是 OI 里真正扣分的地方。

① `cin >> n` 之后直接 `getline`,读到的是空行

cin >> n 读完那个数就停,它后面的换行还留在缓冲区里。 ★ 所以紧接着那句 getline(cin, s) 读到的就是「换行之前」—— 一个空字符串。不报错,但你以为读进了一行正文。
改成
int n;
cin >> n;
cin.ignore();          // ★ 把残留的换行扔掉
string s;
getline(cin, s);  // 这下读到的才是正文
★★ cin >> 和 getline 混着用时,中间补一句 cin.ignore();。

② `0 - 1` 在无符号数里会绕回一个天文数字

size() 返回的是无符号数,无符号数装不下负数。 ★ 于是 int n = v.size(); 之后 n - 1 若为 −1,拿它去 v[...] 或和别的量比,行为就别扭了 —— 编译器只给一句「signedness」警告。
改成
// 循环次数和长度打交道,一律先转 int:
for (int i = 0; i < (int)v.size(); i++) { ... }

// 判断「没元素」别写 v.size() - 1,直接写 v.empty()
★★ 和 int 比就 (int)s.size();判空用 empty(),别用 size() == 0 以外的花活。

③ `freopen` 重定向之后,交到评测机上程序读不了你的文件

freopen("in.txt", "r", stdin); 会让程序去当前目录找 in.txt。 ★ 本地方便,但交到洛谷 / 比赛的评测机上,那台机器上根本没有 in.txt —— 程序直接读不到输入、得 0 分。
改成
// 想留着本地调试的两行,就注释掉;交评测前务必删干净
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
★★ freopen 是本地调试的临时手段:上机前删掉或注释掉。临时注释是常用做法 —— 下次本地调试再打开。

本书哪一章真的用到它

2 处

⚠ 下表里的文件路径和那串 probe 是逐字验过的(check:docs 第 ⑳ 条)—— 改了那份代码这儿会红,正是要的。点章号直接去看那一页。

章书里的哪一份代码用它来干什么
第 1 章code/01-recursion-intro/gen.cpp★★ 第一章的程序就这两行开头 —— 本书所有代码都长这样
第 1 章code/01-recursion-intro/gen.cpp★★ 有了这行,正文里的 cout 才不用写成 std::cout

例题(2 道)

每道都给全套:题面 → 样例 → 参考代码 → 一步步的讲解。先自己读题想一遍,再看代码。

例题 ① 读一个数,再读一整行

io-getline.cpp

第一行一个整数 n。第二行一句话(可能带空格)。输出这句话和它的长度。

★ 这一题就是陷阱 ① 的现场:读完 n 之后能不能顺利读到那句话。

输入格式第一行 n。第二行一句话。
输出格式那句话,以及它的长度。

输入

3
hello world

输出

hello world
长度:11
读 n 之后补 cin.ignore();,getline 才能拿到 hello world。
io-getline.cpp参考代码
// STL 专题 · C++ 上路 例题 —— 读一个数,再读一整行
//
// ★ cin >> n 之后直接 getline 会读到空行:n 后面的换行还留着。
// 中间必须 cin.ignore() 把它扔掉。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
cin.ignore(); // ★ 扔掉残留的换行
string s;
getline(cin, s);
cout << s << '\n';
cout << "长度:" << (int)s.size() << '\n'; // size() 无符号,打印时转 int 更直观
return 0;
}
点一下即可编辑
输入(stdin)
输出
点「运行 ▶」看结果
这段代码是怎么想出来的
  1. 读数:int n; cin >> n;
  2. 扔掉残留的换行:cin.ignore(); —— 没有它,下一步读到的是空行。
  3. 读一行:string s; getline(cin, s);
  4. 输出:cout << s << "\n" << s.size() << "\n"; —— 长度直接是无符号的,要和 int 一起算再转 (int)。
记住这一句

★★★ cin >> 之后要 getline,中间必须 cin.ignore();。

用到的写法:#include <bits/stdc++.h> using namespace std; cin >> n; cin.ignore(); getline(cin, s);

例题 ② 用 auto 遍历一个数组

auto-loop.cpp

读 n 个整数,用「一个名字挨个走一遍」的写法输出它们,每个前面标个序号。

输入格式第一行 n。第二行 n 个整数。
输出格式每行一个「序号: 值」。

输入

4
10 20 30 40

输出

0: 10
1: 20
2: 30
3: 40
auto 推出来的类型是 int&(因为加了 &,是引用,不用拷)。
auto-loop.cpp参考代码
// STL 专题 · C++ 上路 例题 —— 用 auto 遍历一个数组
//
// ★ auto 是「类型编译器自己推」:auto x : a 里 auto 就是 int。
// 改动原数组就写 auto &x : a。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> a(n);
for (int &x : a) cin >> x;
int i = 0;
for (auto x : a) cout << i++ << ": " << x << '\n';
return 0;
}
点一下即可编辑
输入(stdin)
输出
点「运行 ▶」看结果
这段代码是怎么想出来的
  1. 读进 vector:vector<int> a(n); for (int &x : a) cin >> x;
  2. auto 遍历:int i = 0; for (auto x : a) cout << i++ << ": " << x << "\n";
  3. ★ auto x : a 里 auto 就是 int;想改动原数组就写 auto &x : a。
记住这一句

★★ auto 只是把类型名省掉了 —— 推出来是什么,和手写那个类型等价。

用到的写法:auto x : a; for (int &x : a) vector<int> a(n);

练习(3 道)

只给题面、样例和一句提示,答案是折叠着的 —— 先自己写一版。

练习 ① 一整行里有几个词

words-in-line.cpp

读入一整行(可能有多个空格),输出其中有多少个词。

输入格式一行,若干个词,空格分隔。
输出格式词的个数。

输入

  the   quick   brown  fox

输出

4
提示

★ 用 stringstream 把整行当输入流,一个个 >> 读词:stringstream ss(s); string w; while (ss >> w) cnt++;。也可以数空格,但连续空格会数错 —— 前者不依赖分隔细节。

words-in-line.cpp参考答案
记住这一句

★★ 「一行里的词」先 getline 整行,再用 stringstream 一词词读。

用到的写法:getline(cin, s); stringstream ss(s); ss >> w;

练习 ② 字符串的长度和最后一个字符

size-print.cpp

读入一个字符串,输出它的长度、第一个字符和最后一个字符。

输入格式一行一个字符串(不含空格)。
输出格式长度、第一个字符、最后一个字符,各一行。

输入

hello

输出

5
h
o
提示

★ 长度是 (int)s.size();第一个字符 s[0],最后一个 s[(int)s.size() - 1]。⚠ s 为空时 s[0] 就越界了 —— 题目保证非空,真实场景先判。

size-print.cpp参考答案
记住这一句

★★ 下标从 0 到 size()-1;size() 是无符号的,下标运算前转 int。

用到的写法:s.size(); s[0]; cin >> s;

练习 ③ 把读写换成文件:freopen 的用法

file-redir.cpp

读 n 个整数,输出它们的和。这份代码用 freopen 之后,本地调试时输入就从 in.txt 读、答案写进 out.txt,读写代码一行不用改。

★ 交评测前那两行 freopen 必须删掉或注释掉(陷阱 ③)。

输入格式第一行 n。第二行 n 个整数。
输出格式它们的和。

输入

4
3 1 4 1

输出

9
提示

★ 在 main 最开头加 freopen("in.txt", "r", stdin); 和 freopen("out.txt", "w", stdout); —— 之后 cin 自动从 in.txt 读、cout 自动写进 out.txt。⚠ 文件路径要对:相对路径是「程序运行时的当前目录」,不是源文件的目录。

file-redir.cpp参考答案
记住这一句

★★ freopen 改的是「数据从哪来、往哪去」,代码本身一个字都不用动。

用到的写法:freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); cin >> x; cout << s;

做完了去哪儿

这是第一块 · 回 STL 专题目录 · 下一块:前置知识:比较器和「把函数当参数传」 →

⚠ 本页说的是 STL 怎么用。算法本身(Dijkstra、单调栈、状压……)在53 章正课里,而每一章末尾都会告诉你它用到了哪些 STL。