代码仓库shanchuann/CPP-Learninng

标准输入输出流

C++ 标准输入输出流(I/O 流)通过 <iostream> 等头文件封装为面向对象的流对象,是程序与外部设备(键盘、终端、文件)交互的核心机制。相较于 C 语言,C++ 流对象兼具安全性与易用性,支持类型安全的输入输出、灵活的格式控制及动态内存管理。本文聚焦 C++ 标准 I/O 流的核心概念、常用接口及实践示例,帮助开发者快速掌握并正确使用 C++ 标准 I/O 功能。

标准输入流

C++ 标准输入以 std::cin 为核心(istream 类的实例),默认与 C 语言 stdin 同步(可混用 C 风格输入),采用 “行缓冲” 机制 —— 仅当输入换行符 \n 时,键盘输入的字符才会同步到 cin 的内存缓冲区。

核心概念

  • 缓冲区与同步:cin 有独立内存缓冲区,默认通过 std::cin.sync_with_stdio(true)stdin 同步;取消同步(设为 false)可提升输入效率,但禁止混用 C 风格输入(如 scanf)。

  • 缓冲流程:键盘输入→硬件缓存→(遇 \n)→cin 内存缓冲区→输入方法读取。

  • 空白字符处理:默认跳过开头空白字符(空格、Tab、\n),但可通过流控制符调整(如 noskipws 不跳过空白字符)。

常用输入方式

cin >>
格式化输入
  • 核心用途:按变量类型(整数、浮点数、字符串)读取数据,支持链式调用。
  • 关键特点:默认跳过开头空白字符,遇到空白字符停止读取,残留 \n 在缓冲区;类型安全(无需手动指定格式符)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
using namespace std;

int main() {
// cin >> 读取整数与字符串
cout << "Please input two integer and a string(cin): ";
int a, b;
cin >> a >> b; // 输入示例:10 20 hello
cout << "a = " << a << ", b = " << b << endl;

// 读取字符串(不包含空格)
cout << "Please input a string: ";
char str[20];
cin >> str; // 若输入"hello world",仅读取"hello"
cout << "str = " << str << endl;

// 清理 cin >> 残留的换行符(避免影响后续输入)
getchar();

// 流控制符:noskipws(不跳过空白字符)、skipws(恢复跳过空白字符,默认)
cout << "Please input a character(no skip white space): ";
char c, d;
cin >> noskipws >> c; // 会读取空格、\n 等空白字符(如输入" ",则c为空格)
cout << "c = " << c << endl;

cout << "Please input a character: ";
cin >> skipws >> d; // 恢复默认,跳过开头空白字符
cout << "d = " << d << endl;

return 0;
}
  • 注意事项cin >> 无法读取含空格的完整字符串;若输入类型不匹配(如输入字母给整数),cin 会进入 fail 状态,需用 cin.clear() 恢复并清理缓冲区。
cin.get()
单字符 / 指定长度读取
  • 核心用途:读取单个字符或指定长度的字符串,支持保留空白字符,提供三种重载形式。
  • 关键特点:不跳过空白字符;读取指定长度时丢弃终止符(默认 \n),不残留缓冲区;输入超长度时,剩余字符残留缓冲区(cin 状态正常)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
using namespace std;

int main() {
// 清理前序操作残留的换行符
getchar();

// 1. 重载1:cin.get() -> 读取单个字符,返回int(含EOF)
cout << "Please input a character(cin.get()): ";
char e;
e = cin.get(); // 读取单个字符(含空格、\n)
cout << "e = " << e << endl;
getchar(); // 清理残留换行符

// 2. 重载2:cin.get(char& ch) -> 读取单个字符存入ch,返回cin对象
cout << "Please input a character(cin.get()): ";
char f;
cin.get(f); // 读取结果存入f
cout << "f = " << f << endl;
getchar(); // 清理残留换行符

// 3. 重载3:cin.get(char* buf, int n, char delim='\n') -> 读取指定长度字符串
cout << "Please input a string(cin.get()): ";
char str1[20];
// 最多读取19个字符(预留'\0'),遇'\n'停止,丢弃'\n'
cin.get(str1, sizeof(str1), '\n');
cout << "str1 = " << str1 << endl;
getchar(); // 清理残留换行符

return 0;
}

cin.get() 读取字符串时,若输入长度超过 n-1(n 为缓冲区大小),剩余字符会残留缓冲区,等待下次读取。

cin.getline()
指定长度行读取(字符数组)
  • 核心用途:读取一整行字符串(含空格)到字符数组,自动处理终止符(默认 \n)。
  • 关键特点:丢弃终止符 \n(不残留缓冲区);若输入长度超过 n-1,cin 会进入 fail 状态(需手动恢复),与 cin.get(char* buf, int n)差异显著。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std;

int main() {
// cin.getline() 读取字符数组
cout << "Please input a string(cin.getline()): ";
char str2[20];
cin.getline(str2, sizeof(str2)); // 输入示例:hello c++ world
cout << "str2 = " << str2 << endl;

// 结合字符串输入的完整示例
const int n = 10;
char str[n];
cout << "Please input a string(cin >> 后接 cin.getline()): ";
cin >> str; // 先读取不含空格的字符串
cout << str << endl;
getchar(); // 清理 cin >> 残留的'\n'

cout << "Please input a string(cin.getline()): ";
cin.getline(str, sizeof(str)); // 再读取含空格的整行
cout << str << endl;

return 0;
}
std::getline()
动态字符串行读取
  • 核心用途:读取一整行字符串(含空格)到 std::string 对象,需包含<string>头文件,支持动态内存分配(无溢出风险)。
  • 关键特点:丢弃终止符 \n(不残留缓冲区);一次性读完缓冲区数据,无长度限制;与 cin >> 混用时需清理残留 \n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <string> // 必须包含,否则无法使用std::string
using namespace std;

int main() {
// 清理前序操作残留的'\n'
getchar();

cout << "Please input a string(getline()): ";
string str3; // 动态字符串,无需指定长度
getline(cin, str3); // 输入示例:I love C++ programming
cout << "str3 = " << str3 << endl;

// 特殊场景:缓冲区开头有'\n'的处理
cout << "Please input a string(after residual '\\n'): ";
getchar(); // 模拟前序操作残留'\n',先清理
getline(cin, str3);
cout << "str3 = " << str3 << endl;

return 0;
}

cin >> 后直接调用 std::getline()cin >> 残留的 \n 会被std::getline()读取为空白行,需用getchar()cin.ignore() 清理缓冲区。

标准输出流

C++ 标准输出通过<iostream>提供三个核心流对象:std::cout(正常输出)、std::cerr(错误输出)、std::clog(日志输出),均为 ostream 类实例,缓冲特性与用途差异显著。

核心流对象对比

流对象 缓冲特性 核心用途 输出时机
cout 行缓冲 正常程序结果输出(如计算结果) 遇 \n、endl 或 fflush 时
cerr 无缓冲 错误信息输出(如文件打开失败) 数据写入后立即输出
clog 行缓冲 日志信息输出(如调试日志) 遇 \n 或 fflush 时

常用输出方式

cout <<:格式化输出
  • 核心用途:按变量类型输出数据,支持链式调用与精细格式控制(需 <iomanip> 头文件)。
  • 关键特点:默认行缓冲,endl 等价于 “换行 + 强制刷新缓冲区”(效率低于 \n);支持通过 fixed、setprecision 等控制浮点数格式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <iomanip> // 用于格式控制(fixed、setprecision)
using namespace std;

int main() {
int a = 123;
float b = 3.14159;
char s[] = "Hello, C plusplus!";

// 基本类型输出
cout << "Number a = " << a << endl; // 输出整数:Number a = 123
// 浮点数格式化(保留2位小数)
cout << "Number b = " << fixed << setprecision(2) << b << endl; // 输出:Number b = 3.14
// 字符串输出
cout << "String s = " << s << endl; // 输出:String s = Hello, C plusplus!
// 直接输出字符串常量
cout << "Hello, C plusplus!" << endl; // 输出:Hello, C plusplus!

// 循环输出示例
int i = 10;
while (i > 0) {
cout << "i = " << i << endl; // 逐行输出i的值(10~1)
i--;
}
cout << "END" << endl;

return 0;
}
cerr:错误信息输出
  • 核心用途:输出程序运行中的错误信息(如文件打开失败、参数非法),无缓冲特性确保错误信息优先显示(即使程序崩溃)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
using namespace std;

int main() {
// 模拟文件打开失败场景
char errorMsg[] = "File open FAILED!";
cerr << "[Error] " << errorMsg << endl; // 立即输出错误信息:[Error] File open FAILED!

// 即使后续进入循环,错误信息已提前输出
int i = 3;
while (i > 0) {
i--;
}

return 0;
}
clog:日志信息输出
  • 核心用途:输出程序运行日志(如调试信息、状态记录),行缓冲特性减少 I/O 开销(适合批量输出)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;

int main() {
// 批量输出日志信息
clog << "Log: Program started." << endl; // 日志1:程序启动
clog << "Log: Reading configuration..." << endl; // 日志2:读取配置
clog << "Log: Configuration loaded successfully." << endl; // 日志3:配置加载成功

// 循环日志示例
for (int i = 0; i < 2; i++) {
clog << "Log: Processing task " << i + 1 << endl; // 逐任务输出日志
}

clog << "Log: Program ended." << endl; // 日志4:程序结束
return 0;
}

C++ 字符串输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <string>
using namespace std;

int main() {
const int n = 10;
char charStr[n];
string str;

// 1. cin >> 读取无空格字符串
cout << "1. Please input a string(no space, cin >>): ";
cin >> charStr;
cout << "Result: " << charStr << endl;
getchar(); // 清理残留'\n'

// 2. cin.getline() 读取含空格字符串(字符数组)
cout << "2. Please input a string(with space, cin.getline()): ";
cin.getline(charStr, sizeof(charStr));
cout << "Result: " << charStr << endl;

// 3. cin.get() 读取指定终止符字符串
cout << "3. Please input a string(ends with '@', cin.get()): ";
cin.get(charStr, n, '@'); // 遇'@'停止,最多读9个字符
cout << "Result: " << charStr << endl;
getchar(); // 清理残留的'@'和'\n'

// 4. std::getline() 读取动态字符串
cout << "4. Please input a string(dynamic, std::getline()): ";
getline(cin, str);
cout << "Result: " << str << endl;

return 0;
}

其他

const编译策略

在C++中,定义const int a = 10后,经过一系列指针操作,在监视器中得到的结果如下图所示

image.png

但输出却是

1
a = 10, *p = 100, b = 10

这是因为C++会将我们定义的常变量在编译阶段替换为他们的值如下

1
2
3
4
5
6
7
const int a = 10;
int* p = (int*)&a;
int b = 0;
*p = 100;
b = 10;
printf("a = %d, *p = %d, b = %d\n", 10,*p, b);
return 0;

虽然a的值可以替换为100,但在编译阶段已经被替换

当我们将文件后缀替换为.c后,得到输出如下

1
a = 100, *p = 100, b = 100

预定义宏

C++ 编译器内置一组预定义宏,可通过 cout 直接输出,用于获取源文件信息、编译时间、代码位置及 C++ 标准版本,是调试定位与版本管理的实用工具。

代码行 预定义宏 功能说明
cout << __FILE__ << endl; __FILE__ 输出当前源文件的完整路径或文件名(如 D:\code\test.cpp,取决于编译器)
cout << __DATE__ << endl; __DATE__ 输出代码的编译日期(格式:Mmm dd yyyy,如 Oct 16 2025)
cout << __TIME__ << endl; __TIME__ 输出代码的编译时间(非运行时间,格式:hh:mm:ss,如 09:45:30)
cout << __LINE__ << endl; __LINE__ 输出当前 LINE 所在的代码行号(如代码中该宏在第 6 行,则输出 6)
cout <<__func__<< endl; __func__ 输出当前所在函数的名称(此处为 main,C++11 及以后标准支持)
cout << __cplusplus << endl; __cplusplus 输出编译器支持的 C++ 标准版本号(如 201703L 对应 C++17,202002L 对应 C++20)
1
2
3
4
5
6
7
8
int main() {
cout << __FILE__ << endl;
cout << __DATE__ << endl;
cout << __TIME__ << endl; //记录编译时间
cout << __LINE__ << endl;
cout << __func__ << endl;
cout << __cplusplus << endl;
}

输出

1
2
3
4
5
6
D:\Code\C++code\CPP-Learninng\io.cpp
Oct 14 2025
15:52:28
411
main
199711