C++和C语言分别使用不同的头文件和方法来处理输入输出(I/O)操作。
<iostream> 流式I/O<iostream> 是C++标准库中的核心头文件,提供了基于“流”(stream)的输入输出功能。流是一种抽象,代表数据的来源或目的地。
// C++代码中包含头文件
#include <iostream>
// 通常需要配合命名空间使用
using namespace std; // 或者显式使用 std::cin, std::cout
它定义了标准输入流对象 cin (通常关联键盘)、标准输出流对象 cout (通常关联屏幕) 和标准错误流对象 cerr。
<stdio.h> 或 C++中的 <cstdio> 函数式I/OC语言使用 <stdio.h> (Standard Input/Output) 头文件提供输入输出功能。在C++程序中,为了兼容性或特定需求,也可以使用其C++版本 <cstdio>。
// C语言代码中包含头文件
#include <stdio.h>
// C++代码中包含C兼容头文件
#include <cstdio>
using namespace std;
它提供了 scanf() (格式化输入) 和 printf() (格式化输出) 等核心函数。
<< 和 >> 操作符),易于扩展自定义类型的I/O。虽然可以在C++程序中混合使用两者,但为了代码风格的一致性和可维护性,通常建议在项目中选择一种主要方式。
cin / cout 基本用法C++使用输入流提取运算符 >> 和输出流插入运算符 << 进行操作。
#include <iostream>
#include <string> // 处理字符串需要包含
using namespace std;
int main() {
int age;
double price;
string name;
cout << "请输入姓名: "; // 提示用户输入
cin >> name; // 从cin读取字符串到name (遇空白停止)
cout << "请输入年龄和价格: ";
cin >> age >> price; // 链式输入,按顺序读取
// 链式输出
cout << "你好, " << name
<< "! 年龄: " << age
<< ", 价格: " << price << endl; // endl 插入换行并刷新缓冲区
return 0;
}
运算符 >> 和 << 的方向形象地表示了数据的流动方向。
<iomanip>)通过包含 <iomanip> 头文件,可以使用流操纵符来控制输出格式。
#include <iostream>
#include <iomanip> // 使用setw等需要包含此头文件
using namespace std;
int main() {
double pi = 3.1415926535;
int number = 123;
cout << "默认输出 PI: " << pi << endl;
// 控制精度和宽度
cout << "设置精度(4): " << fixed << setprecision(4) << pi << endl; // fixed: 固定小数位数
cout << "设置宽度(10): [" << setw(10) << pi << "]" << endl; // 默认右对齐
cout << "左对齐: [" << left << setw(10) << pi << "]" << endl;
cout << "填充字符('*'): [" << setfill('*') << setw(10) << right << number << "]" << endl; // right恢复右对齐
// 控制进制
cout << "十进制: " << dec << number << endl;
cout << "十六进制: " << hex << showbase << number << endl; // showbase 显示前缀 0x
cout << "八进制: " << oct << number << endl;
return 0;
}
常用操纵符:setw(), setprecision(), fixed, scientific, left, right, setfill(), dec, hex, oct, showbase等。
cin >> var; 默认会跳过输入流中的前导空白符(空格、制表符、换行符)。getline(cin, str_variable); 读取包含空格的一整行到字符串。注意之前若有 cin >> 操作,可能需要先用 cin.ignore() 清除缓冲区中的换行符。cout 输出通常是缓冲的。endl 会插入换行并刷新缓冲区,而只用 '\n' 仅插入换行符,效率可能更高,但不保证立即显示。if (cin.fail()) { ... })来判断输入是否成功。scanf / printf 基本用法C语言使用 scanf() 读取格式化输入,printf() 进行格式化输出。
#include <stdio.h>
int main() {
int age;
double price;
char name[50]; // C风格字符串需要预定义大小
printf("请输入姓名: ");
scanf("%s", name); // 读取字符串,遇空白停止,【注意:有缓冲区溢出风险!】
printf("请输入年龄和价格: ");
// 【重要】scanf需要传入变量的地址(&)
scanf("%d %lf", &age, &price); // %d: int, %lf: double
// %s: 字符串, %d: 整数, %.2lf: double保留两位小数, \n: 换行
printf("你好, %s! 年龄: %d, 价格: %.2lf\n", name, age, price);
return 0;
}
关键点:scanf 需要变量的地址(除了数组名本身代表地址外),而 printf 需要变量的值。
printf 和 scanf 的行为由格式字符串精确控制。格式说明符以 % 开始。
| 格式符 | 对应类型 (printf/scanf) | 说明 |
|---|---|---|
%d 或 %i |
int |
有符号十进制整数 |
%u |
unsigned int |
无符号十进制整数 |
%ld |
long |
长整型 |
%lld |
long long |
长长整型 (C99/C++11) |
%f |
float (printf), float* (scanf) |
单精度浮点数 (printf 中 float 会提升为 double) |
%lf |
double (printf - 可选), double* (scanf - 必须) |
双精度浮点数 |
%Lf |
long double* (scanf) |
长双精度浮点数 |
%c |
int (printf - 接收 char), char* (scanf) |
单个字符 |
%s |
char* (指向C风格字符串) |
字符串 (遇空白停止) |
%p |
void* |
指针地址 |
%% |
无 | 输出一个 '%' 字符 |
注意scanf中 %f, %lf, %Lf 的区别!
printf 的高级格式控制可以在 % 和格式符之间加入修饰符来控制宽度、精度、对齐等。
#include <stdio.h>
int main() {
int num = 42;
double pi = 3.14159;
// 宽度与对齐 (默认右对齐)
printf("宽度5: [%5d]\n", num); // 输出: [ 42]
printf("宽度5,左对齐: [%-5d]\n", num); // 输出: [42 ]
// 精度 (对浮点数是小数位数,对整数是最小数字位数,对字符串是最大字符数)
printf("Pi保留2位小数: %.2f\n", pi); // 输出: 3.14
printf("整数补零宽度5: [%05d]\n", num); // 输出: [00042]
printf("字符串最多3字符: %.3s\n", "Hello"); // 输出: Hel
// 组合使用: 宽度8, 保留3位小数, 左对齐
printf("组合: [%-8.3f]\n", pi); // 输出: [3.142 ]
// 带符号显示
printf("带符号: [%+d]\n", num); // 输出: [+42]
printf("带符号: [%+d]\n", -num); // 输出: [-42]
return 0;
}
scanf 的注意事项& 获取变量地址(数组名除外)。%s 读取字符串时,若输入超过缓冲区大小,会造成严重安全问题。建议使用 %<宽度>s (如 %49s 读取到大小为50的数组) 或更安全的函数(如 fgets)。scanf 中,多数格式符(如 %d, %lf, %s)会自动跳过前面的空白符。但 %c 不会跳过空白符,它会读取遇到的第一个字符,包括空格或换行符。可以在 %c 前加一个空格 (" %c") 来跳过空白。scanf 返回成功读取并赋值的项数。可以利用此返回值检查输入是否符合预期。int items_read = scanf("%d %lf", &a, &b);
if (items_read != 2) {
printf("输入错误!\n");
// 处理错误,例如清空缓冲区
int c;
while ((c = getchar()) != '\n' && c != EOF); // 清空到行尾
}
C的标准I/O也是带缓冲的。
printf 的输出通常是行缓冲(遇到换行符 \n 时刷新)或全缓冲(缓冲区满时刷新)。可以使用 fflush(stdout); 强制刷新标准输出缓冲区,确保内容立即显示。scanf 从标准输入缓冲区读取。如果缓冲区中有残留字符(特别是换行符),可能会影响后续的 scanf 或 getchar() 调用。上面示例中的 while ((c = getchar()) != '\n' && c != EOF); 是一种常见的清空输入缓冲区到行尾的方法。| 特性 | C++ cin / cout |
C scanf / printf |
|---|---|---|
| 语法简洁度 | 较高 (链式调用, 操作符重载) | 较低 (函数调用, 格式字符串) |
| 类型安全 | 高 (编译器自动处理类型) | 低 (需手动匹配格式符与类型) |
| 易用性 | 对初学者友好,不易出错 | 需要记忆格式符,易因地址、类型错误导致问题 |
| 执行效率 | 通常稍慢 (涉及对象构造/析构、虚函数等开销),但可通过关闭同步(sync_with_stdio(false))和解绑(cin.tie(nullptr))大幅提速 |
通常更快 (直接函数调用,底层优化),尤其在简单类型上 |
| 格式控制 | 灵活 (通过操纵符),但有时略显繁琐 | 非常灵活 (格式字符串功能强大),但也更复杂 |
| 错误处理 | 面向对象方式 (检查流状态) | 函数返回值 (检查读取项数) |
| 扩展性 | 高 (易于为自定义类重载 << 和 >>) |
低 (不易扩展到自定义类型) |
| 安全性 | 相对较高 (cin >> string 自动管理内存) |
较低 (scanf %s 易缓冲区溢出,需谨慎使用) |
在时间效率要求极高的竞赛中:
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
可以解除 C++ 流与 C 标准 I/O 的同步,并解绑 cin 和 cout,使其性能接近甚至超过 scanf/printf。但需注意,之后不能再混合使用 C 和 C++ 的 I/O 函数。且 endl 的刷新开销仍在,大量输出时用 '\n' 可能更快。
在教学、练习或一般 C++ 项目中:
scanf 的许多陷阱(类型不匹配、忘记&、缓冲区溢出)。选择哪种输入输出方式取决于具体场景和需求。对于现代C++编程,cin/cout 因其类型安全、易用性和面向对象的特性,通常是首选。而在对性能要求极致或需要 C 语言兼容性的场合,scanf/printf 仍然是有效的工具,但使用时需格外小心。
掌握两者的用法和优缺点,能够帮助你根据实际情况做出最佳选择。