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
仍然是有效的工具,但使用时需格外小心。
掌握两者的用法和优缺点,能够帮助你根据实际情况做出最佳选择。