- 翠微暑期集训
快读
- @ 2026-10-7 16:56:34
不喜勿喷
C++ 新手教学:从 cout 到快读
一、最开始的输入输出:cin 和 cout
1.1 基本用法
刚学 C++ 时,我们一般这样写:
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n; // 输入一个整数
cout << n << '\n'; // 输出这个整数并换行
return 0;
}
cin是标准输入流,>>是流提取运算符。cout是标准输出流,<<是流插入运算符。- 可以连续输入输出:
int a, b;
cin >> a >> b;
cout << a << " " << b << '\n';
cin 和 cout 的好处:
- 不用记格式符,比如
%d、%lld。 - 类型安全,输入输出会自动匹配类型。
- 写起来简单,适合新手。
1.2 为什么说它慢?
cin / cout 默认会跟 C 语言的标准输入输出(scanf / printf)保持同步,还会在输入前刷新输出缓冲区。这些机制保证了混用时的正确性,但也带来了额外的开销。数据量很大时(比如读入 (10^6) 个整数),就可能超时。
1.3 小技巧:'\n' 和 endl 的区别
cout << n << '\n'; // 只是输出一个换行符
cout << n << endl; // 输出换行符,并刷新缓冲区
endl 会强制刷新缓冲区,频繁刷新会导致程序变慢。
所以平时尽量用 '\n',少用 endl。
二、C 风格输入输出:printf 和 scanf
2.1 为什么需要它们?
scanf / printf 是 C 语言的输入输出函数,通常比默认的 cin / cout 快。它们直接和底层打交道,没有 C++ 流的那些复杂机制。
使用时需要包含头文件:
#include <cstdio>
2.2 scanf 的基本用法
int n;
scanf("%d", &n);
%d表示读入一个int。&n是取地址,scanf需要知道变量存在哪里,才能把读到的值写进去。- 忘记写
&是新手常见错误,会导致程序崩溃或乱码。
读多个变量:
int a, b;
scanf("%d%d", &a, &b);
读 long long:
long long x;
scanf("%lld", &x);
读 double:
double d;
scanf("%lf", &d);
读字符:
char ch;
scanf(" %c", &ch); // 前面的空格可以跳过空白字符
读字符串(不含空格):
char s[100];
scanf("%s", s); // 数组名本身就是地址,不用加 &
2.3 printf 的基本用法
int n = 10;
printf("%d\n", n);
%d表示输出一个int。\n是换行符。- 可以一次输出多个:
int a = 1, b = 2;
printf("%d %d\n", a, b);
输出 long long:
long long x = 1234567890123LL;
printf("%lld\n", x);
输出 double:
double d = 3.14159;
printf("%.2f\n", d); // 保留两位小数
输出字符和字符串:
char ch = 'A';
char s[] = "hello";
printf("%c\n", ch);
printf("%s\n", s);
2.4 常见格式符总结
| 类型 | scanf 格式符 | printf 格式符 |
|---|---|---|
| int | %d |
|
| long long | %lld |
|
| char | %c |
|
| double | %lf |
%f |
| float | %f |
|
| 字符串 | %s |
|
注意:double 输入用 %lf,输出用 %f,不要弄混。
2.5 注意事项
scanf的格式符必须和变量类型匹配,否则会读错。printf的格式符也必须匹配,否则输出可能乱码。scanf中除了%c以外的格式符会自动跳过前面的空白字符(空格、换行、制表符)。printf不会自动换行,需要手动加\n。
三、让 cin/cout 变快:解绑同步流
如果你还是喜欢用 cin / cout,但又嫌它慢,可以在 main 函数开头加上这两句:
ios::sync_with_stdio(false);
cin.tie(nullptr);
完整示例:
#include <iostream>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
cout << n << '\n';
return 0;
}
3.1 这两句是什么意思?
-
ios::sync_with_stdio(false);
关闭 C++ 标准流和 C 标准流的同步。默认情况下,cin/cout会和scanf/printf共享缓冲区,保证混用时顺序正确。关闭后,cin/cout可以独立使用自己的缓冲区,速度会快很多。 -
cin.tie(nullptr);
解除cin和cout的绑定。默认情况下,每次从cin读数据之前,都会自动刷新cout的缓冲区。解除绑定后,cin不再自动刷新cout,减少了不必要的刷新操作。
3.2 注意事项
- 解绑之后,不要混用
cin/cout和scanf/printf。因为两者不再同步,输出顺序可能会乱。 - 如果一定要混用,就不要解绑。
cout.tie(nullptr);一般不用写,因为cout很少需要绑定到其他流。
3.3 速度对比
解绑后,cin / cout 的速度已经接近 scanf / printf,对于大多数题目足够了。但如果题目卡常数特别严,还是需要手写快读。
四、手写快读:getchar 版本
4.1 getchar 是什么?
getchar() 是 C 语言的一个函数,从标准输入读取一个字符,返回它的 ASCII 码(整数)。读到文件结尾返回 EOF。
#include <cstdio>
int main() {
char ch = getchar(); // 读一个字符
return 0;
}
4.2 快读的原理
整数在输入中是一串字符,比如 123 是 '1'、'2'、'3'。
我们可以逐个字符读取,跳过空格和换行,遇到数字就把它拼成整数:
x = x * 10 + (ch - '0');
比如读 123:
- 初始
x = 0 - 读
'1':x = 0 * 10 + 1 = 1 - 读
'2':x = 1 * 10 + 2 = 12 - 读
'3':x = 12 * 10 + 3 = 123
这就是快读的核心。
4.3 基础整数快读
#include <cstdio>
inline int read() {
int x = 0, f = 1;
char ch = getchar();
// 跳过非数字字符,同时处理负号
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
// 读取数字
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x * f;
}
使用:
int n = read();
int m = read();
解释:
inline建议编译器内联,减少函数调用开销。f是符号,遇到'-'就变成-1。- 第一个
while跳过所有非数字字符,包括空格、换行、负号。 - 第二个
while处理连续的数字字符。 - 最后返回
x * f,如果是负数就乘以-1。
4.4 处理负数
上面的代码已经处理了负数:当遇到 '-' 时,f = -1,最后 return x * f。
注意:如果输入中可能有 '-' 出现在数字前面,第一个 while 会先读到 '-',设置 f = -1,然后继续读下一个字符,直到读到数字。
4.5 支持 long long
int 的范围大约是 (-2 \times 10^9) 到 (2 \times 10^9)。如果数字更大,需要读 long long。
inline long long readLL() {
long long x = 0;
int f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x * f;
}
使用:
long long x = readLL();
4.6 模板版本
如果不想为每种类型写一个函数,可以用模板:
template <typename T>
inline T read() {
T x = 0;
int f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x * f;
}
使用:
int n = read<int>();
long long m = read<long long>();
这样就能自动适配不同类型。
4.7 更快的 fread 版本(进阶)
getchar() 本身还是函数调用,每次读一个字符。如果数据量特别大,可以用 fread 一次读一大块到缓冲区,再从缓冲区里取字符。
#include <bits/stdc++.h>
using namespace std;
namespace FastIO {
const int BUF = 1 << 20; // 1MB 缓冲区
char buf[BUF];
int p = 0, len = 0;
inline int gc() {
if (p == len) {
len = (int)fread(buf, 1, BUF, stdin);
p = 0;
if (len == 0) return EOF;
}
return buf[p++];
}
template <typename T>
inline T read() {
T x = 0;
int f = 1;
int c = gc();
while (c != '-' && (c < '0' || c > '9')) c = gc();
if (c == '-') {
f = -1;
c = gc();
}
while (c >= '0' && c <= '9') {
x = x * 10 + (c - '0');
c = gc();
}
return x * f;
}
}
using FastIO::read;
int main() {
int n = read<int>();
long long m = read<long long>();
printf("%d %lld\n", n, m);
return 0;
}
这个版本比 getchar 更快,但代码复杂一些。新手可以先掌握 getchar 版本,以后再学 fread 版本。
4.8 使用示例
#include <cstdio>
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x * f;
}
int main() {
int n = read();
int m = read();
printf("%d %d\n", n, m);
return 0;
}
输入:
10 20
输出:
10 20
五、速度对比与选择建议
| 方法 | 速度 | 易用性 | 适用场景 |
|---|---|---|---|
cin / cout 默认 |
慢 | 最简单 | 数据量小,练习 |
scanf / printf |
较快 | 较简单 | 一般竞赛 |
cin / cout 解绑 |
简单 | 喜欢 C++ 流 | |
getchar 快读 |
快 | 中等 | 数据量大,卡常 |
fread 快读 |
最快 | 较难 | 极限卡常 |
建议:
- 新手先熟练
cin/cout。 - 然后学
scanf/printf,了解格式符。 - 再学解绑同步流,让
cin/cout变快。 - 最后学
getchar快读,应对大数据。 fread快读可以以后再看。
六、完整代码模板(getchar 快读)
#include <cstdio>
// 快读:支持 int
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x * f;
}
// 快读:支持 long long
inline long long readLL() {
long long x = 0;
int f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x * f;
}
int main() {
int n = read();
long long m = readLL();
printf("%d %lld\n", n, m);
return 0;
}
这样从 cout 到 printf,再到解绑同步流,最后到 getchar 快读,每一步都讲清楚了。你可以按这个顺序慢慢学,先理解原理,再动手写几遍,就能掌握快读了。