scanf和cin在大量整数读取时拖慢程序,根本原因是二者做了过多冗余工作:cin默认同步stdio并维护流状态、locale解析等;scanf需解析格式字符串、处理多类型转换。而竞赛只需“字符转int”,裸getchar_unlocked手写快读可提速3–5倍。

为什么 scanf 和 cin 在大量整数读取时会拖慢程序
当输入规模达到 10⁵ 级别以上(比如 OJ 上的图论/数据结构题),cin 默认同步 stdin 并带缓冲区检查,scanf 又要解析格式字符串、处理浮点/符号/进制等通用逻辑——这两者都会成为瓶颈。实测在 2×10⁵ 个整数输入下,cin 可能比裸 getchar_unlocked 慢 3–5 倍。
根本原因不是“C++ 慢”,而是标准 IO 函数做了太多你不需要的事:线程安全、locale 解析、错误重试、流状态维护。而竞赛/高频读取场景只要“把一串数字字符转成 int”这一件事。
用 getchar_unlocked 手写整数读取函数要注意什么
getchar_unlocked 是 GNU 扩展(glibc 提供),跳过 FILE* 锁检查,比 getchar 快约 20–30%,但仅限单线程环境(OJ 都是单进程单线程,放心用)。它不保证跨平台(MSVC 不支持),编译需加 -D_GNU_SOURCE 或直接用 GCC/Clang。
手写读取函数的关键点:
立即学习“C++免费学习笔记(深入)”;
- 跳过所有非数字字符(空格、换行、制表符),直到遇到
'-'或数字 - 记录符号位,然后逐字符累积:
res = res * 10 + (c - '0'),注意溢出判断(通常题目保证不溢出,可省) - 必须用
int c接收返回值,因为EOF是-1,而char可能截断为255 - 不要在循环里调用
isdigit(c)——它查 locale 表,比c >= '0' && c 慢;同理避免 <code>isblank
一个轻量可靠的实现:
inline int read_int() {
int c = getchar_unlocked(), x = 0, f = 1;
while (c != '-' && (c < '0' || c > '9')) c = getchar_unlocked();
if (c == '-') f = -1, c = getchar_unlocked();
while (c >= '0' && c <= '9') {
x = x * 10 + (c - '0');
c = getchar_unlocked();
}
return x * f;
}
read_int() 和 fread + 自解析缓冲区哪个更快
当输入超大(如 10⁶+ 整数),反复调用 getchar_unlocked 仍有函数调用开销和分支预测失败成本。此时可预读整块内存,再用指针扫描解析——也就是 “fread + buffer parsing” 方案。
典型做法:
- 分配 1fread(buf, 1, sizeof(buf)-1, stdin)
- 用指针
char *p = buf扫描,跳过空白、识别符号、累加数字,比每次系统调用快一个数量级 - 需手动处理缓冲区边界(
p >= buf_end时重新 fread)和末尾残留(如最后一个数没换行) - 注意:不能混用
fread和getchar_unlocked,后者底层仍走 stdio 缓冲,会冲突
简单场景(≤ 5×10⁵ 整数)用 read_int() 足够;上百万级且卡常严格时才值得上 fread 缓冲方案。
关闭同步后 cin 还能抢救吗
如果不想写 C 风格代码,cin 也能提速:在 main() 开头加两行:
ios::sync_with_stdio(false); cin.tie(nullptr);
前者解除 cin 与 stdio 的绑定,后者解绑 cin 和 cout(避免每次 cin 后 flush cout)。这时 cin >> x 速度接近 scanf,但依然比 read_int() 慢 1.5–2 倍——因为内部仍有格式解析和异常标志检查。
注意:sync_with_stdio(false) 后不能再混用 scanf/printf 和 cin/cout,否则行为未定义;调试时若用 freopen 重定向 stdin,也要在 sync 调用前做。
真正卡常的场合,手写 read_int 是最可控的选择;但它的代价是失去类型安全和自动错误处理——比如输入了字母,read_int 会静默跳过或卡死,而 cin 可以通过 failbit 检测。



















