C++ 高性能输入输出优化实战
在算法竞赛或高并发场景下,cin/cout 和 scanf/printf 往往成为性能瓶颈。当数据量达到百万级时,标准 IO 可能直接导致超时。这时候,手写快读快写就成了必备技能。
基础版:getchar / putchar
最基础的优化是利用字符缓冲流。相比 scanf,直接读取字符并手动解析整数能减少大量函数调用开销。
支持负数的整数快读
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;
}
无符号整数快读(更快)
inline unsigned int read() {
unsigned int x = 0;
char ch = getchar();
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x;
}
整数快写(递归实现)
inline void write(int x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
进阶版:缓冲区优化
如果数据量极大,频繁的系统调用依然是瓶颈。利用 fread 和 fwrite 一次性读写大块内存,可以显著降低 I/O 等待时间。
静态缓冲区快读
char buf[1 << 21], *p1 = buf, *p2 = buf;
inline char get_char() {
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
}
inline int read() {
int x = 0, f = 1;
char ch = get_char();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = get_char();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = get_char();
}
return x * f;
}
缓冲区快写与刷新
char pbuf[1 << 21], *pp = pbuf;
inline void push(const char& c) {
if (pp - pbuf == (1 << 21)) {
fwrite(pbuf, 1, 1 << 21, stdout);
pp = pbuf;
}
*pp++ = c;
}
inline void write(int x) {
static int sta[35];
int top = 0;
if (x < 0) {
push('-');
x = -x;
}
do {
sta[top++] = x % 10;
x /= 10;
} while (x);
while (top) push(sta[--top] + '0');
push('\n');
}
inline void flush() {
fwrite(pbuf, 1, pp - pbuf, stdout);
pp = pbuf;
}
注意:程序结束前需调用 flush() 确保剩余数据写入。
扩展类型与命名空间封装
针对 long long 或字符串也有对应的实现。为了代码整洁,通常会将这些功能封装进命名空间。
长整型读取
inline long long readll() {
long long 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;
}
一行字符串读取
inline void readstr(char* str) {
char ch = getchar();
while (ch == ' ' || ch == '\n') ch = getchar();
while (ch != ' ' && ch != '\n' && ch != EOF) {
*str++ = ch;
ch = getchar();
}
*str = '\0';
}
终极优化版本(Namespace 封装) 这种写法将输入输出逻辑完全隔离,方便复用且不易污染全局命名空间。
namespace FastIO {
const int SZ = 1 << 20;
char inbuf[SZ], outbuf[SZ];
int in_left = 0, in_right = 0;
int out_right = 0;
inline void load() {
int len = fread(inbuf, 1, SZ, stdin);
in_left = 0;
in_right = len;
}
inline char get_char() {
if (in_left >= in_right) load();
if (in_left >= in_right) return EOF;
return inbuf[in_left++];
}
inline int read() {
int x = 0, f = 1;
char ch = get_char();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = get_char();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = get_char();
}
return x * f;
}
inline void flush() {
fwrite(outbuf, 1, out_right, stdout);
out_right = 0;
}
inline void put_char(char ch) {
outbuf[out_right++] = ch;
if (out_right == SZ) flush();
}
inline void write(int x) {
if (x < 0) {
put_char('-');
x = -x;
}
if (x > 9) write(x / 10);
put_char(x % 10 + '0');
}
struct Flusher {
~Flusher() { flush(); }
} flusher;
}
using FastIO::read;
using FastIO::write;
using FastIO::put_char;
使用示例
int main() {
int n = read(); // 读取一个整数
write(n); // 输出一个整数
put_char('\n'); // 输出换行
// 使用命名空间版本
int m = FastIO::read();
FastIO::write(m);
FastIO::put_char('\n');
return 0; // Flusher 会在程序结束时自动调用 flush()
}
性能对比与建议
实际测试中,不同版本的提升幅度大致如下:
- 基础版 (
getchar/putchar):比scanf/printf快约 2-3 倍。 - 缓冲区版 (
fread/fwrite):比基础版再快 2-3 倍。 - 终极优化版:在极端大数据量下表现最佳,比缓冲区版再快约 1.5 倍。
对于大多数算法竞赛题目,基础版或缓冲区版已经足够应对。只有面对千万级数据输入的极限情况,才建议启用终极优化版本。合理选择方案,别让 IO 拖了算法的后腿。

