cin / cout 入门:>> 到底怎么切数据

cin>>a>>b 凭什么知道 10 20 是两个数、10,20 就崩了?把缓冲区拆开看一遍。

GESPCSP-J

◎学完本节你会

1拆开缓冲区:cin >> a >> b 是怎么切数据的

选一种输入,点「单步」。下面每个格子是缓冲区里的一个字符,你会看到 >> 怎么跳过空白(黄格)、怎么读数字、又在哪里停下来。

int a, b; cin >> a; // 第 1 次读取 cin >> b; // 第 2 次读取
尚未开始
缓冲区(每格一个字符):
空白字符 当前正在看 已被吃掉 卡在这里
int a
?
int b
?
流状态:goodbit

2流一旦失败,后面全都读不进来

这是新手最难自己想通的一点。看下面的实测结果——注意 b、c 的值。

int a = -111, b = -111, c = -111; // -111 是"哨兵值",用来区分"没被赋过值" cin >> a >> b >> c; cout << a << " " << b << " " << c;
输入abc发生了什么
10 20 30102030正常
10 x 30100-111读 b 撞上 x → 失败置 0;读 c 时流已 fail,压根没执行赋值
x 20 300-111-111第一次就失败,后面两个全没动
99999999999999 12147483647-111-111溢出 int → 失败,值饱和到 int 最大值(C++11 起的行为)
关键结论(g++ 实测):读取失败时,① 格式失败(撞上非数字字符)会把该变量置为 0; EOF 失败(数据不够、读到末尾)则保持原值不变;② 两种失败都会给流标记 failbit; ③ 之后所有 >> 都直接跳过,不再给变量赋值(所以 b、c 还是哨兵 -111)。 这不是 bug——设计意图是"一旦输入不合预期,就别再往下猜了"。
怎么恢复:cin.clear(); 清掉 failbit,再用 cin.ignore(1000,'\n'); 把缓冲区里那个"坏字符"所在的整行丢掉,才能继续读。忘了 clear() 就直接 ignore() 是没用的。
if(!(cin >> x)){ // 推荐:直接检查读取是否成功 cin.clear(); // ① 清除失败标记 cin.ignore(1000,'\n'); // ② 丢掉这一行 cout << "请输入整数\n"; }

3cout:输出什么,由变量的类型决定

点一个表达式,看输出。<< 可以一直串下去,每个 << 输出一项,中间不会自动加空格。

cout << 1 << ' ' << 2;
1 2

endl 与 '\n' 的区别

写法做的事什么时候用
'\n'只往缓冲区塞一个换行字符竞赛一律用这个(快,见 3.6 节实测差 18 倍)
endl塞换行 + 立刻 flush(把缓冲区全部写出去)需要马上看到输出时(调试)
为什么 endl 慢:每次 flush 都是一次真实的系统写操作。循环里输出 100 万行, endl 就 flush 100 万次。3.6 节会用实测数据展示这个差距。

!最容易踩的 4 个坑

① 用逗号分隔的输入配 >>:输入 10,20,cin>>a>>b 只能读到 a=10,b 失败置 0。要么改输入格式,要么用 char 吃掉逗号(见实验 1 的"逗号"用例)。
② 忘记 #include <iostream>:报 'cout' was not declared in this scope。 同理忘记 using namespace std; 就得写 std::cout。
③ 读失败后不 clear() 就继续读:后面所有输入全部读不进来, 程序看起来"卡住了"或输出一堆 0。这是"明明输入对的却 WA"的头号原因之一。
④ 把 << 写成 >>:方向反了。cin >> x 是流入变量, cout << x 是流出到屏幕。记法:箭头指向数据要去的地方。

?跨学科:为什么"缓冲区"这个概念到处都是

你键盘敲的字并不是"一个字符一个字符"直接送给程序的。操作系统把它们先攒在缓冲区里, 等你按下回车,程序才开始从缓冲区里取——这样你还能用退格键修改,程序也不用为每个按键醒一次。

输出同理:cout 先攒着,攒够一批或遇到 endl/程序结束才真正写到屏幕。 这叫缓冲 I/O,本质是用"攒批"换取吞吐——和水龙头接水一样,接满一桶再倒,比一滴一滴倒快得多。

这个思想在工程里无处不在:数据库的写缓冲、网络的 TCP 缓冲、日志的批量落盘、 甚至 CPU 的写回缓存,都是同一个权衡。3.6 节的"加速三行",本质就是调整这套缓冲策略。

✎练一练

先自己想,再展开对照。
答案:a=15,b=25。 空格是分隔符,>> 会自动跳过它。用换行、制表符分隔同样可以——>> 不区分是哪种空白。
提示:逗号不是空白字符。
答案:8 0。
>>a 读到 8,停在逗号处(逗号留在缓冲区);>>b 看到逗号,不是数字 → 失败,b 置 0,流进入 failbit。

想正确读逗号分隔:
int a,b; char comma; cin >> a >> comma >> b; // 用 char 把逗号"吃掉"
实测输入 10,20 时得到 a=10、comma=','、b=20。
两个数看着都是数字,但小数点是个麻烦。
答案:a=3,b=0(实测)。

推导:>>a 读 3,遇到 . 停下——小数点留在缓冲区,a=3。 >>b 从 . 开始,它既不是空白也不是数字 → 失败,b=0,failbit。 后面的 14 2.5 再也没人读了。

要点:>> 到 int 时不会帮你"四舍五入"或"跳过小数点",它就是遇到第一个非数字字符就停。 要读小数,变量类型必须是 double。类型不匹配 = 静默失败,编译器不会警告你。