cin>>a>>b 凭什么知道 10 20 是两个数、10,20 就崩了?把缓冲区拆开看一遍。
cin >> 的三条切分规则:跳过空白、读到非数字就停、停下的那个字符留在缓冲区;10,20 为什么只能读进一个数,以及流失败后为什么后面全都读不进来;cout << 链式输出,并知道同一个 65 输出成 65 还是 A 由什么决定。cin >> a >> b 是怎么切数据的选一种输入,点「单步」。下面每个格子是缓冲区里的一个字符,你会看到 >>
怎么跳过空白(黄格)、怎么读数字、又在哪里停下来。
这是新手最难自己想通的一点。看下面的实测结果——注意 b、c 的值。
| 输入 | a | b | c | 发生了什么 |
|---|---|---|---|---|
| 10 20 30 | 10 | 20 | 30 | 正常 |
| 10 x 30 | 10 | 0 | -111 | 读 b 撞上 x → 失败置 0;读 c 时流已 fail,压根没执行赋值 |
| x 20 30 | 0 | -111 | -111 | 第一次就失败,后面两个全没动 |
| 99999999999999 1 | 2147483647 | -111 | -111 | 溢出 int → 失败,值饱和到 int 最大值(C++11 起的行为) |
failbit;
③ 之后所有 >> 都直接跳过,不再给变量赋值(所以 b、c 还是哨兵 -111)。
这不是 bug——设计意图是"一旦输入不合预期,就别再往下猜了"。cin.clear(); 清掉 failbit,再用 cin.ignore(1000,'\n');
把缓冲区里那个"坏字符"所在的整行丢掉,才能继续读。忘了 clear() 就直接 ignore() 是没用的。点一个表达式,看输出。<< 可以一直串下去,每个 << 输出一项,中间不会自动加空格。
| 写法 | 做的事 | 什么时候用 |
|---|---|---|
| '\n' | 只往缓冲区塞一个换行字符 | 竞赛一律用这个(快,见 3.6 节实测差 18 倍) |
| endl | 塞换行 + 立刻 flush(把缓冲区全部写出去) | 需要马上看到输出时(调试) |
endl 就 flush 100 万次。3.6 节会用实测数据展示这个差距。>>:输入 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 读到 8,停在逗号处(逗号留在缓冲区);>>b 看到逗号,不是数字 → 失败,b 置 0,流进入 failbit。10,20 时得到 a=10、comma=','、b=20。>>a 读 3,遇到 . 停下——小数点留在缓冲区,a=3。
>>b 从 . 开始,它既不是空白也不是数字 → 失败,b=0,failbit。
后面的 14 2.5 再也没人读了。>> 到 int 时不会帮你"四舍五入"或"跳过小数点",它就是遇到第一个非数字字符就停。
要读小数,变量类型必须是 double。类型不匹配 = 静默失败,编译器不会警告你。