🔷 第二章 C++语言基础
📄 一句话概括

C++ 把输入输出抽象成"流"——cout 是一个输出流对象、cin 是一个输入流对象,<< 和 >> 是往流里"推"和从流里"抽"的动作。cin 有三条解析规则:① 自动跳过前导空白(所以空格和回车都能当分隔符);② 按目标变量的类型读(读 int 遇到字母就停);③ 读不下去就"罢工"——流进入失败状态,后面所有读取全部失效。最后一条是隐藏最深的坑,也是"输入必须校验"的根因。

一、流是什么:一块"数据通道"

cout << "Hello";        // cout 是输出流对象,<< 把数据推进去
int a;
cin >> a;               // cin 是输入流对象,>> 把数据抽出来

技术上的叫法(认识一下就行):

为什么 cin >> a 不用写 &a(C 语言要):因为 C++ 用引用机制把变量直接交给 cin(第 7 章讲引用)。

二、缓冲区:为什么要"先攒着"

你敲了一串字符,按回车
        ↓
先进入【缓冲区】(一块临时内存)
        ↓
cin 从缓冲区里按需要取

缓冲区带来的三个现象:

  1. 可以一次输入多个值:7 3 一次性喂给 cin >> a >> b
  2. 空格与回车等价:它们都只是"分隔符",所以输入 7 3、7 3、7 回车 3 结果一样
  3. 多余的内容留在缓冲区:等你下一次读取——这既是便利(连续读),也是坑(比如残留的回车会影响后面的读取)

三、cin 的三条解析规则

规则一:自动跳过前导空白

int a, b;
cin >> a >> b;      // 输入 "  7    3  " 也没问题,空白自动跳过

空白包括:空格、制表符(Tab)、换行符。

规则二:按目标变量的类型解析

目标类型 它会怎么读 遇到什么停
int 找一串数字 遇到字母、小数点、空白就停
double 找数字,认小数点和指数 遇到字母停
char 读一个非空白字符 立刻停(注意:会跳过空白!)
bool 只认 0 和 1 其他都失败

规则三:读不下去就"罢工"(这条最重要)

int a, b, c;
cin >> a >> b >> c;      // 你输入:7 abc 9

会发生什么:

  1. a 得到 7 —— 正常
  2. 读 b 时遇到 abc,解析失败 → b 拿到一个不确定的值(C++11 后置 0)
  3. 流进入失败状态:cin "卡住了",后面读 c 直接失败,c 也不会被赋值
  4. 而且这个失败状态会一直保持,直到你手动清除(cin.clear())

所以真实程序必须校验输入(第 4 章会用循环"逼用户重新输入")。一个输错,后面全乱——这就是本节最值钱的一句话。

四、endl 与 "\n" 的区别

cout << "abc" << endl;      // 换行 + 【刷新缓冲区】
cout << "abc" << "\n";      // 只换行,不强制刷新
动作
endl 换行 + 立刻把缓冲区内容送出去(flush)
"\n" 只换行(什么时候真正写出去由系统决定)

什么时候在意:程序崩溃时,还没刷出去的内容会丢——这也是 cerr 设计成"不缓冲"的原因(2.1.2)。入门阶段都用 endl 就好,等追求性能时再换 "\n"。

五、动手 Demo:三个实验看清规则

新建 stream2.cpp:

#include <iostream>
using namespace std;

int main() {
    int a, b;

    // 实验一:空格与回车等价
    cout << "输入两个整数(空格或回车分开):";
    cin >> a >> b;
    cout << "a = " << a << "  b = " << b << endl;

    // 实验二:读字符会跳过空白
    char c;
    cout << "随便输几个字符,中间夹空格:";
    cin >> c;
    cout << "读到的是:" << c << "(空白被跳过了)" << endl;

    // 实验三:故意输错,看流罢工
    int x, y;
    cout << "输入两个整数,但第二个故意输字母:";
    cin >> x >> y;
    cout << "x = " << x << "  y = " << y << "(y 很可能不是你想的)" << endl;
    return 0;
}

看到什么算成功: - 实验一两行用不同分隔方式,结果完全一样 - 实验二里即使你在字符前打了空格,读到的仍是那个字符 - 实验三里 y 拿到一个"不对的值"(在 C++11 之后通常是 0),说明流在失败后不再正常工作

30 秒实验:在实验三之后再加一句 cin >> x;,看它能不能读到东西——读不到(流还卡在失败状态)。要恢复必须 cin.clear(); 并清掉缓冲区里的残留字符。

六、常见误区

  1. "cin >> a >> b 必须让用户按两次回车"——不用。空格和回车都能分隔
  2. "输入错了重新输一次就行"——不行。流会进入失败状态并卡住,必须显式清除(cin.clear())
  3. "cin >> c 能读到空格"——不能,空白被跳过了。要读含空格的整行,得用 getline(第 6 章)
  4. "endl 和 \n 完全一样"——endl 多做了"刷新缓冲区"这件事。大多数情况没差别,但崩溃丢输出的场景下差别很致命
自测四问(点开对答案) 1. `cin` 为什么会自动跳过空格?——因为**空白(空格/Tab/回车)只是分隔符**,`cin` 会跳过前导空白再开始解析。 2. `cin >> a >> b` 输入 `7 3` 与 `7` 回车 `3` 有区别吗?——**没有**,两者等价。 3. 输入类型不符时会怎样?——**流进入失败状态**:当前变量拿到不确定值,**后续所有读取都失败**,直到调用 `cin.clear()` 清除。 4. `endl` 和 `"\n"` 的区别?——`endl` = 换行 **+ 刷新缓冲**;`"\n"` 只换行。

相关笔记