C++ 快读快写详解:读入整数与读入字符串
温馨提示:点击右下角的目录按钮可快速跳转到你想要的哦。
整数读入输出
众所周知,快读快写是众多 OI 选手必须掌握的一项卡常小技巧。优秀的读入输出可以让你的代码跑得飞快,有时甚至能比别人多得一些分数。那么如何写快读快写呢?那就引入下面这道题:题目跳转:P10815 【模板】快速读入。
在此注明,这不是一篇关于 P10815 【模板】快速读入 的题解(当然也算沾点边),只是借用它来展现各种读入输出方法的速度对比。
引入
读入 n n n 个数,求它们的和。每个测试点时限 2.5 s 2.5s 2.5s。
非常简单,对吧?但是 n ≤ 1 × 10 8 n\leq 1\times 10^8 n≤1×108,怎么读入这么多数成了难题。
直接这样写:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,x,sum;
int main(){
cin>>n;
for(ll i=1;i<=n;i++){
cin>>x;
sum+=x;
}
cout<<sum;
return 0;
}
读入 10 8 10^8 108 个数据竟然需要惊人的 67.526 s 67.526s 67.526s,这谁能接受?
先在此声明,本文在本地跑出来的时间均为在 Windows 下使用 DEV-C++ 编译后跑出来的时间,且只跑一组 10 8 10^8 108 的数据得出;在洛谷上的时间则为所有洛谷提供的数据评测出来的时间。
先看一组对比数据:
- 读入对比(以 1 M i B 1 MiB 1MiB
fread为 1 1 1)
| 方法 | 相对耗时 | 说明 |
|---|---|---|
fread 大块读 |
1.0 1.0 1.0 | 通常最快或接近最快;块 IO,库/系统调用开销被摊薄 |
cin.read 解绑 |
1.05 ∼ 1.6 1.05\sim 1.6 1.05∼1.6 | 块 IO,接近 fread;需自行缓冲并处理 gcount() |
getchar_unlocked |
4 ∼ 10 4\sim 10 4∼10 | 逐字符无锁;常配合手写整数解析,通常很快 |
getchar |
8 ∼ 20 8\sim 20 8∼20 | 逐字符有锁和 stdio 开销;比 getchar_unlocked 慢 |
关流的 cin>> |
20 ∼ 60 20\sim 60 20∼60 | 关闭同步并 tie(nullptr) 后明显变快,但仍是流式 token 格式化提取 |
普通 scanf |
20 ∼ 70 20\sim 70 20∼70 | 有 stdio 锁和格式串解析;读整数通常比普通 cin>> 快 |
普通 cin>> |
50 ∼ 150 50\sim 150 50∼150 | 未关同步,含同步/刷新等开销,通常最慢之一 |
- 写出对比(以 1 M i B 1 MiB 1MiB
fwrite大块写为 1 1 1)
| 方法 | 相对耗时 | 说明 |
|---|---|---|
fwrite 大块输出 |
1.0 1.0 1.0 | 通常最快或接近最快;需先构造缓冲区 |
cout.write 解绑 |
1.05 ∼ 1.8 1.05\sim 1.8 1.05∼1.8 | 块 IO,接近 fwrite;需自己管理输出缓冲区 |
putchar |
10 ∼ 30 10\sim 30 10∼30 | 逐字符有锁;写整数需手写转字符串 |
关流的 cout<< |
20 ∼ 50 20\sim 50 20∼50 | 关闭同步并 tie(nullptr) 后较快,但仍有格式化/类型处理开销 |
普通 printf |
20 ∼ 50 20\sim 50 20∼50 | 每次格式化有解析开销和 stdio 锁;大块 %s 会更快 |
普通 cout<< |
50 ∼ 150 50\sim 150 50∼150 | 未关同步,通常明显慢于关流 cout<< 和普通 printf |
这里解释一下 cin.read 和 cin.get 到底是什么,其实你可以这样理解:
-
cin.read类似fread,是块读; -
cout.write类似fwrite,是块写。
以下按“从常见、易写到较快”的顺序介绍。不同平台、编译器、IO 环境下排名可能不同,表中相对耗时只表示数量级参考,不代表严格排名:
一、关闭同步流后的 cin、cout
这应该是许多 OI 选手在考场上最喜欢用的快读方式,因为它够用且非常好写。
关闭同步流后,cin/cout 与 scanf/printf 不再保证同步,千万不要混用。若必须混用,应重新打开同步并注意刷新,或全程只使用一套 IO。
但是它通过不了此题,本地实际读入 10 8 10^8 108 的数据需要 10.951 s 10.951s 10.951s,在洛谷上花了 6.11 s 6.11s 6.11s,点此查看,是无法接受的。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,x,sum;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin>>n;
for(ll i=1;i<=n;i++){
cin>>x;
sum+=x;
}
cout<<sum;
return 0;
}
二、C 风格输入的优化之 getchar
这就是非常实用的 getchar 读入方式,它是通过先读入字符,然后再转成数字的形式进行读入加速的。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll rd(){
ll x=0,f=1;
char ch=getchar();
while(ch!=EOF&&(ch<'0'||ch>'9')){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
if(ch==EOF){
return x;
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch-'0');
ch=getchar();
}
return x*f;
}
void write(ll x){
if(x<0){
x*=-x;
putchar('-');
}
if(x>9){
write(x/10);
}
putchar(x%10+'0');
}
ll n,sum;
int main(){
n=rd();
for(ll i=1;i<=n;i++){
sum+=rd();
}
write(sum);
return 0;
}
非常出乎意料,本地读入 10 8 10^8 108 的数据,getchar 居然用了 17.476 s 17.476s 17.476s!比关同步流还慢!在洛谷上花了 4.20 s 4.20s 4.20s(比关流稍快),点此查看,所以使用 getchar 的方式通过不了此题。
三、C 风格输入的优化之 getchar_unlocked
getchar_unlocked 是 getchar 的非线程安全版本,少了加锁开销,因此通常更快。正式比赛能否使用要看评测环境。
Linux 下一般用 getchar_unlocked;Windows/MinGW 下通常没有 getchar_unlocked,可用 _getchar_nolock 替代,写的时候要注意,下面的代码使用了宏定义的一些技巧,在 Windows 和 Linux 两种环境下都不会 CE。
代码
#include<bits/stdc++.h>
using namespace std;
#ifdef __unix__
#define gc getchar_unlocked
#else
#define gc _getchar_nolock
#endif
typedef long long ll;
ll rd(){
ll x=0,f=1;
char ch=gc();
while(ch!=EOF&&(ch<'0'||ch>'9')){
if(ch=='-'){
f=-1;
}
ch=gc();
}
if(ch==EOF){
return x;
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch-'0');
ch=gc();
}
return x*f;
}
void write(ll x){
if(x<0){
x*=-x;
putchar('-');
}
if(x>9){
write(x/10);
}
putchar(x%10+'0');
}
ll n,sum;
int main(){
n=rd();
for(ll i=1;i<=n;i++){
sum+=rd();
}
write(sum);
return 0;
}
此方法在本地跑 10 8 10^8 108 的数据花费 4.499 s 4.499s 4.499s,在洛谷上跑出了 3.21 s 3.21s 3.21s 的成绩,
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/2401_88456980/article/details/170584862



