C++ 快速输入方法
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
序章、目录
> 第一部分:总领全文
>
>
> 第二部分:方法详解
>
> > 1. 优化标准输入流CIN
> >
> > 2. 使用 C 语言的 SCANF 函数
> >
> > 3. 批量读取缓冲区
> >
> > 4. 使用快速 IO 库
> >
> > 5. 使用自定义函数
>
>
> 第三部分:速度对比
>
>
> 第四部分:注意事项
>
>
> 第五部分:后记
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
一、总领全文
感谢北大西洋公约 · NATO,没有他就没有这篇文章。
因为我看不懂他的一篇题解。
@北大西洋公约 · NATO
众所有周知,在做某一些题目或比赛中经常爆TLE,结果换了个更快的输入方法就解决了(比如我)
我真的服了,花的时间直接从3000ms变成几百ms。可见输入的重要性。
后续还有增加。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
二、方法详解
1. 优化标准输入流(CIN)
原理
> 1.cin并非独立的输入工具,它组合了各种其他的东西和繁琐的步骤。
> 它只是为了兼容 C 语言的scanf 和getchar(),就与 stdio 共享同一个底层输入缓冲区,它>每次读取都需要进行“同步效验”,同步效验的过程如下:
>
> * 当调用cin>>n 时,cin首先会检查底层输入缓冲区中是否有 scanf/getchar() 未读取完的数>据?
> * 如果有,先将底层输入缓冲区的数据同步到 cin 自己的临时缓冲区(避免数据重复读取);
> (典型先为别人考虑,不考虑自己,值得表扬)
> * 如果没有,再从输入设备读取数据到底层输入缓冲区,然后同步到 cin 临时缓冲区;
> * 同步完成后,cin才能从自己的临时缓冲区解析数据到变量n。
>
> 离谱就离谱在你没有使用scanf 和getchar()它也要执行这几步(死脑筋)
>
> 2. 然后还有,你连续输入的话也会消耗时间,举个例子,就比如你一直开灯关灯开灯关灯...那灯丝不会烧坏啊。
>
> 3. 它还与cout绑定,cin 和 cout 默认是 “绑定(Tied)” 关系,这是为了保证 “输出提示语” 和 “输入操作” 的顺序正确性。例如:
>
> 默认绑定机制下,执行 cin>>n前,会强制触发 cout.flush()(刷新 cout 的输出缓冲区),确保 “请输入数字:” 先显示在屏幕上,再等待用户输入。
> 但不需要的时候还是会检查。
>
> 4. 接着,scanf有格式控制符不用考虑类型,而cin还要判断输入的数据符不符合类型。
> 不仅如此,它还维护了状态位,有两个缓冲区……
> 啊这……
代码
优缺点
优点 缺点 无需修改原有 cin 使用习惯 无法完全消除底层开销 兼容性好 - 代码简洁 -
2. 使用 C 语言的 SCANF 函数
原理
> scanf 是 C 标准库输入函数,简洁的如同期末时的大脑,因此比未优化的 cin 快。
实现代码
优缺点分析
优点 缺点 速度稳定 需记忆不同类型的格式控制符 代码简洁 类型安全性低 兼容性强 读取字符串需人工设置大小,容易溢出
3. 批量读取缓冲区
原理
> 一次性将大块数据从输入设备读取到内存缓冲区,再从缓冲区中逐字符解析数据。
> 想象你在超市买零食,需要把零食从货架搬到购物袋里:
>
> * 普通读取(如scanf或cin)就像:拿一个零食,跑回购物袋放下,再跑回货架拿下一个... 来来回回跑很多趟,效率很低。
> * 批量读取缓冲区就像:推一辆购物车到货架前,一次把能装的零食零食都装到购物车里,推回购物袋旁再慢慢整理,大大减少了来回跑的次数。
前方高能,请小心食用\color{red}{前方高能,请小心食用}前方高能,请小心食用
代码
优缺点分析
优点 缺点 速度极快,IO 次数最少 代码复杂 适合超大量数据 缓冲区大小需手动调整 可灵活输入其他类型 若输入数据格式异常,会导致解析错误
4. 使用快速 IO 库
原理
部分算法竞赛常用的 “万能库”(如 bits/stdc++.h)或第三方快速 IO 库(如 fastio.h),已预实现优化后的输入函数(如 read()、readint()),底层基于 “批量缓冲区” 或 “getchar() 逐字符读取”,开箱即用。
代码
> 小熊猫2.21不支持(我试过了,不包含)
优缺点分析
优点 缺点 开箱即用 依赖特定库(部分编译器不支持) 支持多种类型,兼容性强 离开特定编译器可能无法运行 底层已做极致优化 不同库的函数名、参数可能不同,需适应
5. 使用自定义函数
某人的题解
代码
inline 是一个关键字,用于建议编译器对函数进行内联展开优化。
内联展开的含义
通常情况下,函数调用会有一定的开销(如保存现场、跳转、恢复现场等)。而内联函数在编译时,编译器会尝试将函数体直接 "嵌入" 到调用它的地方,而不是生成常规的函数调用指令。
比如,如果有这样的内联函数:
当你调用 f(3, 5) 时,编译器可能会直接替换为 3 + 5,而不是生成调用 f 函数的代码。
使用 inline 的要点:
* 提高程序运行速度:消除函数调用的开销,适合频繁调用的小型函数(比如你代码中的 input() 函数,可能会被多次调用)。
* 不增加额外内存开销:如果没有内联,多次函数调用会重复执行相同的调用流程;内联展开后,虽然可能增加代码量(代码膨胀),但避免了重复的调用开销。
注意点
* inline 只是编译器的建议,不是强制命令。如果函数体过大或包含复杂结构(如循环、递归),编译器可能会忽略 inline 关键字,按普通函数处理。
* 内联函数通常需要在头文件中定义(而不是只声明),因为编译器需要在调用点看到完整的函数体才能进行内联展开。
* 过度使用内联可能导致生成的可执行文件变大(代码膨胀),反而影响性能。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
三、速度对比
1. 速度排序(从快到慢)
1. 批量缓冲区读取(自定义):IO 次数最少,内存解析效率最高,速度极致;
2. 自定义 getchar() 逐字符读取:逐字符处理,无批量缓冲区的指针管理,但仍比流操作快;
3. 优化后的cin:消除同步开销,接近 scanf 速度;
4. scanf:C 标准输入,速度稳定但略慢于优化后的 cin;
5. 未优化的 cin:同步开销大,速度最慢,不适合大量数据。
2. 实际测试数据(参考)
在 “读取 100 万条随机整数” 场景下,不同方法的耗时(单位:秒)如下(数据因硬件、编译器不同略有差异):
输入方法 耗时(秒) 相对速度(以 “未优化 cin” 为基准) 批量缓冲区读取 0.08 ~ 0.12 约 10~15 倍快 自定义 getchar() 0.15 ~ 0.20 约 8~10 倍快 优化后的 cin 0.25 ~ 0.30 约 6~8 倍快 scanf 0.30 ~ 0.35 约 5~6 倍快 未优化的 cin 1.2 ~ 1.5 1 倍(基准)
四、注意事项
1. 批量缓冲区读取:需注意缓冲区大小(过小可能需多次 fread,过大浪费内存),且需处理 “缓冲区数据读完” 的补充读取逻辑(本文示例为简化版,实际需判断 it 是否超出缓冲区);
2. scanf 格式控制符:严格匹配变量类型(如 long long 必须用 %lld,而非 %d),否则会导致数据错误;
3. 优化后的cin:解除绑定后,cin 与 printf/scanf 不可混用(可能导致输出顺序混乱);
4. 快速 IO 库:bits/stdc++.h 非标准库,在 VS 等编译器中需手动配置,移植时需注意兼容性。
五、后记
> 我没用AI写了,就查了下资料以自己的理解写的,与真实的可能有差异,希望大家反馈。
> 第一次写,希望点个赞,生活不易,点个赞吧QwQ