前言
在编程中,输入输出(I/O)是交互的基础。C 和 C++ 提供了丰富的机制,从简单的 printf/scanf 到流式操作 cin/cout。看似简单,实则暗藏玄机。缓冲区问题、格式控制陷阱、性能瓶颈都可能成为调试的拦路虎。本文将针对这些常见问题逐一拆解。
1. OJ 题目输入情况汇总
在竞赛环境中,输入场景通常分为以下几类。结合具体题目来看会更直观。
1.1 单组测试用例
例如计算 (a+b)/c 的值。
#include <iostream>
using namespace std;
int main() {
int a, b, c;
cin >> a >> b >> c;
cout << (a + b) / c << endl;
return 0;
}
再比如'与 7 无关的数'。思路是先找出与 7 相关的数取反。条件包括被 7 整除、个位是 7、十位是 7。
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int i = 1, sum = 0;
while (i <= n) {
if (i % 7 != 0 && i % 10 != 7 && i / 10 != 7) {
sum += (i * i);
}
i++;
}
cout << sum << endl;
return 0;
}
1.2 多组测试用例
1.2.1 测试数据组数已知
如果题目明确告诉你有 N 组数据,直接循环 N 次即可。
#include <iostream>
using namespace std;
int main() {
int n; // 表示数据组数
cin >> n;
int a, b;
while (n--) {
cin >> a >> b;
cout << a + b << endl;
}
return 0;
}
对于斐波那契数列,如果范围限制在 30 以内,预处理数组比每次递归或循环更高效。
#include <iostream>
using namespace std;
int main() {
int n, a, i;
int ret[35] = {0, 1, 1};
for (i = 3; i < 30; i++) {
ret[i] = ret[i - 1] + ret[i - 2];
}
cin >> n;
while (n--) {
cin >> a;
cout << ret[a] << endl;
}
return 0;
}
还有一种常见写法是处理 q 次询问,用 while(q--) 包裹单组逻辑。注意字符串下标通常从 0 开始,但题目可能从 1 开始计数,需调整。
1.2.2 测试数据组未知
当不知道有多少组数据时,利用 cin 的返回值特性。
#include <iostream>
using namespace std;
int main() {
int a, b;
while (cin >> a >> b) {
cout << a + b << endl;
}
return 0;
}
原理是 cin >> a >> b 返回流对象引用,成功读取转为 true,遇到 EOF 或错误转为 false。
1.2.3 特殊值结束测试数据
有时以特定值(如 0 0)作为结束标志。
#include <iostream>
using namespace std;
int main() {
int a = 0, b = 0;
while (cin >> a >> b, a && b) {
cout << a + b << endl;
}
return 0;
}
这里利用了逗号表达式,整体结果取决于最后一个表达式。
2. 输入时特殊技巧
2.1 含空格字符串的特殊处理方式
读取带空格的字符串,可以用 fgets、scanf、getchar 或 getline。但如果只需按单词处理,直接用 cin >> s 自动跳过空格更方便。
例如统计数字字符个数,可以一次性读入整个行分析,也可以逐个单词读入。
// 法一:读取整行
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
getline(cin, s);
int ret = 0;
for (auto ch : s) {
if (ch >= '0' && ch <= '9') ret++;
}
cout << ret << endl;
return 0;
}
// 法二:按单词处理
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
int cnt = 0;
while (cin >> s) {
for (auto c : s) {
if (c >= '0' && c <= '9') cnt++;
}
}
cout << cnt << endl;
return 0;
}
判断字符类型时,使用 isdigit(ch) 或 isalpha(ch) 比手动比较 ASCII 更简洁。
2.2 数字的特殊处理方式
输入的数字既可以是整数也可以是字符串。根据需求选择解析方式。 如果是奇偶转换,可以直接对整数取模,也可以当作字符串处理 ASCII 码。
// 法一:当做整数读取
#include <iostream>
#include <cmath>
using namespace std;
int main() {
int n;
int i = 0, ret = 0;
cin >> n;
while (n) {
if (n % 10 % 2 == 1) ret += pow(10, i);
n /= 10;
i++;
}
cout << ret << endl;
return 0;
}
// 法二:当做字符串处理
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
cin >> s;
for (int i = 0; i < s.size(); i++) {
if (s[i] % 2) s[i] = '1';
else s[i] = '0';
}
cout << stoi(s) << endl;
return 0;
}
stoi 是 C++11 引入的函数,用于将字符串转换为整数。
3. scanf/printf 和 cin/cout 的对比
scanf/printf 是 C 语言标准库,cin/cout 是 C++ 流对象。各有优劣。
3.1 格式控制差异
scanf/printf 需要手动指定格式,容易出错但精确;cin/cout 自动识别类型,更安全易用。但在复杂格式输出时,printf 更灵活。
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
float a = 3.50;
double d = 16.50;
cout << "cout: " << a << " " << d << endl;
printf("printf: %f %lf\n", a, d);
return 0;
}
cout 默认忽略多余 0,printf 默认打印 6 位小数。
3.2 性能差异
大数据量下,scanf/printf 通常更快。cin/cout 默认与 C 标准库同步,且绑定刷新,导致开销增加。
优化方案是在程序开头加入:
ios::sync_with_stdio(false);
cin.tie(0);
这能取消同步和绑定,大幅提升速度。
结语
掌握输入输出的底层机制,才能写出健壮高效的代码。无论是缓冲策略还是格式控制,每个细节都影响最终表现。

