【算法竞赛】C/C++ 的输入输出你真的玩会了吗?

【算法竞赛】C/C++ 的输入输出你真的玩会了吗?
在这里插入图片描述
🔭 个人主页:散峰而望

《C语言:从基础到进阶》《编程工具的下载和使用》《C语言刷题》《算法竞赛从入门到获奖》《人工智能AI学习》《AI Agent》

愿为出海月,不做归山云


🎬博主简介

在这里插入图片描述
请添加图片描述

文章目录


前言

在编程的世界中,输入输出(I/O)是与用户或外部系统交互的基础。C 和 C++ 提供了强大的 I/O 机制,从简单的 printf 和 scanf 到 C++ 的流式操作 cin 和 cout,看似简单,实则暗藏玄机。许多开发者在使用过程中可能会遇到缓冲区问题、格式控制陷阱、性能瓶颈,甚至安全性隐患。
本文将对 C/C++ 一些关于在输入输出的问题进行一一打击。

1. OJ(online judge)题目输入情况汇总

在竞赛的 OJ 题目中,一般关于输入场景总结为下面四类:

在这里插入图片描述

由于单独讲解可能体验不是那么深,所以接下来,我们就结合题目,给大家分别介绍。

1.1 单组测试用例

  1. 计算 (a+b)/c 的值
在这里插入图片描述

参考代码:

#include<iostream>usingnamespace std;intmain(){int a, b, c; cin >> a >> b >> c; cout <<(a + b)/ c << endl;return0;}
  1. 与 7 无关的数
在这里插入图片描述
思路
先输入 n,之后循环产生 1~n 的数字,接着找出与 7 无关的正整数,最后求平方和。
那么如何产生与 7 无关的正整数呢?
我们不如先找与 7 相关的正整数,然后取反就能得到与 7 无关的正整数。
又由题目知道 n < 100,所以只需在 100 内的数字进行判断。
即与 7 相关的条件有:被 7 整除,%10 == 7(个位),/10 == 7(十位)

参考代码:

#include<iostream>usingnamespace std;intmain(){int n; cin >> n;int i =1;int sum =0;while(i <= n){if(i %7!=0&& i %10!=7&& i /10!=7){ sum +=(i * i);} i++;} cout << sum << endl;return0;}

1.2 多组测试用例

1.2.1 测试数据组数已知

  1. 多组输入a+b II
在这里插入图片描述

参考代码:

#include<iostream>usingnamespace std;intmain(){int n;//表示数据组数 cin >> n;int a, b;//每行输入的a,b值 while(n--){ cin >> a >> b; cout << a + b << endl;}return0;}
  1. 斐波那契数列
斐波那契数列:1 1 2 3 5 8 13 21 34 55 …
特点:前两个数的和是第三个数
在这里插入图片描述
在这里插入图片描述

斐波那契有多种做题办法

参考代码:

  • 正常的循环嵌套:
#include<iostream>usingnamespace std;intmain(){int n, a; cin >> n;while(n--){ cin >> a;int x =1;int y =1;int z =1;while(a >2){ z = x + y; x = y; y = z; a--;} cout << z << endl;}}
  • 由题目可知,题目把所求范围限制在 1~30 之间,所以提前算出前 30 个斐波那契数,存储下来输入 Q 后,直接拿取第 a 个斐波那契数:
#include<iostream>usingnamespace std;intmain(){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;}return0;}
  • 还有一种方法是递归,后面会进行讲解
  1. 制糊串
在这里插入图片描述
在这里插入图片描述
思路:输入 s,t,qq 次询问
while(q–)
{ }
处理一组数据
输入 l1,r1,l2,r2取出子串 s1,t1比较子串的大小,并输出
字符串的比较 - 支持关系运算的比较

参考代码:

#include<iostream>#include<string>usingnamespace std;intmain(){ string s, t; cin >> s >> t;int q; cin >> q;int l1, r1, l2, r2;while(q--)// 这种写法是常?的处理 q 次询问的方式 { cin >> l1 >> r1 >> l2 >> r2; string s1 = s.substr(l1 -1, r1 - l1 +1);// 注意这道题的字符串是从 1 开始计数的  string t1 = t.substr(l2 -1, r2 - l2 +1);if(s1 < t1){ cout <<"yifusuyi"<< endl;}elseif(s1 > t1){ cout <<"erfusuer"<< endl;}else{ cout <<"ovo"<< endl;}}return0;}
编程技巧:
题目中说"有 q 次询问",意思是程序要处理q组测试数据,(也就是对应 q 次循环),我们要针对每次询问,给出一个结果。
其实就是之前的单组测试变成了 q 组测试,在之前的代码上套一层 while 循环即可。当有 q 次询问的时候,while(q–) 是非常方便的方式。然后就按照单组输入的方式处理每组输入的数据就好了。

1.2.2 测试数据组未知

  1. 多组输入a+b
在这里插入图片描述

参考代码:

#include<iostream>usingnamespace std;intmain(){int a, b;while(cin >> a >> b){ cout << a + b << endl;}return0;}
📌cin >> a; 会返回一个流对象的引用,即 cin 本身。在 C++ 中,流对象 cin 可以被用作布尔值来检查流的状态。如果流的状态良好(即没有发生错误),流对象的布尔值为 true。如果发生错误(如遇到输入结束符或类型不匹配),布尔值为 false。在 while (cin >> a >> b) 语句中,循环的条件部分检查 cin 流的状态。如果流成功读取到 2 个值,cin >> a >> b 返回的流对象 cin 将被转换为 true,循环将继续。如果读取失败(例如遇到输入结束符或无法读取到2个值),cin >> a >> b 返回的流对象 cin 将被转换为 false,循环将停止。
  1. 数字三角形

参考代码:

#include<iostream>usingnamespace std;intmain(){int n;while(cin >> n){for(int i =1; i <= n; i++){for(int j =1; j <= i; j++){ cout << j <<" ";} cout << endl;}}}
  1. 定位查找
在这里插入图片描述
思路:先输入 n,指明要存储多少个数
2.再输入 m,并和输入的数组中的数据进行比较查找
3.只要查找的一出现就输出并跳出循环,如果没有就输出 No

参考代码:

#include<iostream>usingnamespace std;constint N =25;int arr[N];intmain(){int n, m;while(cin >> n){for(int i =0; i < n; i++){ cin >> arr[i];} cin >> m;int i =0;for(i =0; i < n; i++){if(m == arr[i]){ cout << i << endl;break;}}if(i == n){ cout <<"No"<< endl;}}return0;}

1.2.3 特殊值结束测试数据

  1. 字符统计
在这里插入图片描述

参考代码:

//代码1#include<iostream>usingnamespace std;intmain(){int ch =0;int Letters =0;int Digits =0;int Others =0;while((ch =getchar())!='?'){if((ch >='a'&& ch <='z')||(ch >='A'&& ch <='Z')){ Letters++;}elseif(ch >='0'&& ch <='9'){ Digits++;}else{ Others++;}} cout <<"Letters="<< Letters << endl; cout <<"Digits="<< Digits << endl; cout <<"Others="<< Others << endl;return0;}
//代码2#include<iostream>usingnamespace std;intmain(){ string s;int Letters =0;int Digits =0;int Others =0;getline(cin, s); s.pop_back();//去掉? for(auto ch : s){if((ch >='a'&& ch <='z')||(ch >='A'&& ch <='Z')) Letters++;elseif(ch >='0'&& ch <='9') Digits++;else Others++;} cout <<"Letters="<< Letters << endl; cout <<"Digits="<< Digits << endl; cout <<"Others="<< Others << endl;return0;}

这里判断大小写字母数字可以用一种简单的方法来判断,运用函数:

if(islower(ch)||isupper(ch)) Letters++;elseif(isdigit(ch)) Digits++;else Others++;

当然判断字母还能再简化一下

if(isalpha(ch)) Letters++;elseif(ch >='0'&& ch <='9') Digits++;else Others++;

可以拓展了解一下 :isalphaisdigit

  1. 多组数据a+b III
在这里插入图片描述

参考代码:

#include<iostream>usingnamespace std;intmain(){int a =0, b =0;while(cin >> a >> b, a && b){ cout << a + b << endl;}return0;}
这里的逗号表达式简单介绍一下:从左往右依次计算整个表达式的结果是最后一个表达式的结果

OK 了,有关OJ(online judge)题目输入情况的所有情况都讲完了,希望各位友友们在做题中能够有着清晰的判断。

2. 输入时特殊技巧

2.1 含空格字符串的特殊处理方式

根据我们现在掌握的知识,含空格的字符串,如要读取有 fgets、scanf、getchar、getline 四种方式解决,但是有时候,根据题目的情况,不一定非要完整的读取这个带空格的字符串,而是将字符串中空格隔开的每一个字符串,当做一个单词处理更方便,也避免了读取带空格字符串的各种问题。

练习:统计数字字符个数

在这里插入图片描述

这里我们有两种做法:

整个字符串一次性读取,然后处理字符串,使用 getline使用逐个单词的方式处理 cin
  1. 法一:读取整个带空格的字符串分析
#include<iostream>#include<string>usingnamespace std;intmain(){ string s;getline(cin, s);int ret =0;for(auto ch : s){if(ch >='0'&& ch <='9'){ ret++;}} cout << ret << endl;return0;}
  1. 法二:按照多个单词分析
#include<iostream>#include<string>usingnamespace std;intmain(){ string s;int cnt =0;while(cin >> s){for(auto c : s){if(c >='0'&& c <='9') cnt++;}} cout << cnt << endl;return0;}

同时由上面的部分介绍,我们可以把

if(ch >='0'&& ch <='9')

修改为

if(isdigit(ch))

2.2 数字的特殊处理方式

当我们程序运行的时候,在控制台输入123 的时候,这时的 123 是三个字符,123 是一个字符序列,程序会根据代码中的数据类型,可能将123解析成整型,也可能将 123 解析成字符串。

比如:

int num =0; cin >> num;//输入123, 就被解析成整数  string s; cin >> s;//输入123, 就被解析成字符串

这里的解析的方式,主要是依赖编译器对变量类型的识别,根据类型再将读取字符串数据转化成对应类型的数据。

我们在写代码的时候,应该根据实际的情况,来决定如何处理输入的内容。

练习:小乐乐改数字

在这里插入图片描述
在这里插入图片描述
  1. 法一:当做整数读取
输入一个整数 n获取 n 的每一位
% 10,判断是奇数(1)还是偶数(2)
/ 10,求后面的数字
3.输出转换后的整数
#include<iostream>#include<cmath>usingnamespace std;intmain(){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;return0;}
  1. 法二:当做字符串处理
由于读取的是字符,所以 1,2,3 不能当作字符来判断,而是用 ASCII 判断
又 ‘0’ = 48,‘1’ = 49,‘2’ = 50,‘3’ = 51 … 发现 ASCII 为偶数时对应的正好为偶数,奇数时正好为奇数,故可以 %2 判断。
#include<iostream>#include<string>usingnamespace std;intmain(){ 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;// 转换成数字输出 return0;}
这里说一下代码的一些函数pow(a, b) 求 a 的 b 次方stoi 是 C++11 标准引入的字符串转换函数,用于将字符串转换为整数

3. scanf/printf 和 cin/cout的对比

scanf 和 printf 是 C 语言中的标准输入输出函数,而 cin 和 cout 是 C++ 语言中的标准输入输出流对象。它们各自有优缺点,整体上来说 cin 和 cout 会更加方便,但有时候我们也不得不使用 scanf 和 printf。

3.1 格式控制差异

  • scanf 和 printf 不能自动识别输入数据的类型,需要手动指定格式字符串,容易出现格式错误。开发者需要确保格式字符串与变量类型匹配,否则会导致未定义行为。
  • cin 和 cout 会根据变量类型自动处理输入输出,避免格式化错误。相对 scanf 和printf 而且,C++的 cin 和 cout 更加易用。
  • scanf 和 printf:格式化输出更精确直观,特别适合复杂格式的输入输出,比如:在要求指定格式输出的时候,printf 函数就比 cout 更加方便和灵活。
#include<cstdio>#include<iostream>usingnamespace std;intmain(){float a =3.50;double d =16.50; cout <<"cout: "<<a <<" "<< d <<endl;printf("printf: %f %lf\n", a, d);return0;}

输入结果如下:

在这里插入图片描述
区别:cout 默认不会输出六位小数,自动忽略小数点后多余的 0,printf 函数打印浮点
数的时候,小数点默认打印 6 位。cout 在输出的时候不需要指定格式,printf 则需要明确的格式。

3.2 性能差异

3.2.1 案例演示

结论: scanf 和 printf 通常比 cin 和 cout 快。
原因: cin 和 cout 由于要考虑兼容 C 语言的输入和输出,封装实现的更加复杂,通常比 scanf 和 printf 稍慢,但这种差异在大多数应用场景中可以忽略不计。但是在竞赛的题目中,尤其是当输入、输出数据量较大时,使用 cin 和 cout 完成输入输出,经常会出现 Time Limit Exceeded 的情况。而 scanf 和 printf 就不存在类似的问题。下面给大家准备了两个案例。

案例一:数字游戏

代码展示:

  1. 使用 cin 和 cout:
#include<iostream>usingnamespace std;int t, x;intmain(){ cin >> t;while(t--){ cin >> x;int ret =0;while(x){int count =0, high =0;int tmp = x;while(tmp){//计算最右边的1代表的值 int low = tmp &-tmp;//如果low中剩余的1就是最后一个1 //就是最左边的1 if(tmp == low){ high = low;}//去掉最右边的1  tmp -= low; count++;}if(count %2==0){ x -= high;}else{ x ^=1;} ret++;} cout << ret << endl;}return0;}

运行结果展示:

在这里插入图片描述
  1. 使用 scanf 和printf:
#include<iostream>usingnamespace std;int t, x;intmain(){scanf("%d",&t);while(t--){scanf("%d",&x);int ret =0;while(x){int count =0, high =0;int tmp = x;while(tmp){//计算最右边的1代表的值 int low = tmp &-tmp;//如果low中剩余的1就是最后一个1 //就是最左边的1 if(tmp == low){ high = low;}//去掉最右边的1  tmp -= low; count++;}if(count %2==0){ x -= high;}else{ x ^=1;} ret++;}printf("%d\n", ret);}return0;}

运行结果展示:

在这里插入图片描述

案例 2:求第 k 小的数

  1. 使用 cin 和 cout:
#include<iostream>#include<cstdio>#include<algorithm>usingnamespace std;constint N =5000010;int arr[N];intmain(){int n, k; cin >> n >> k;for(int i =0; i < n; i++){ cin >> arr[i];}sort(arr, arr + n); cout << arr[k]<< endl;return0;}

运行结果展示:

在这里插入图片描述
  1. 使用 scanf 和printf:
#include<iostream>#include<cstdio>#include<algorithm>usingnamespace std;constint N =5000010;int arr[N];intmain(){int n, k; cin >> n >> k;for(int i =0; i < n; i++){scanf("%d",&arr[i]);}sort(arr, arr + n); cout << arr[k]<< endl;return0;}

运行结果展示:

在这里插入图片描述

我们可以看到这两个案例中,输入的数据量都比较大,在输入数据的时候如果使用cin,都会出现超时的问题,但是换成是 scanf 的方式就能正确的通过。这就是因为两者性能上的差异导致的。那为什么 cin/cout 的性能要低于 scanf/printf 呢?有什么办法能优化吗?

后面会单独对这个问题进行讲解。

总结一下其实就是 2 个点:

  1. C++ 中为了支持混合使用 cin/cout 和 scanf/printf,C++ 标准库默认会将 cin、cout
    等 C++ 流对象与 stdin、stdout 等 C 标准库的流对象同步在一起。这种同步操作意味着每次使用 cin或cout 时,都会自动刷新 C 标准库的缓冲区,以保 C++ 和 C 的 I/O 是一致的。这就导致了性能的下降。
  2. 在默认情况下,cin 和 cout 之间存在一种绑定关系。这种绑定意味着,每当从 cin 读取数据时,任何之前通过 cout 输出的内容都会被强制刷新到屏幕上。这种绑定也可能导致性能问题,特别是在需要频繁读取大量数据的情况下。

3.2.2 优化方案和演示

通过下面这个案例我们看一下 scanf 和 cin 的差异,然后再看看如何优化。

#include<iostream>#include<ctime>#include<cstdio>usingnamespace std;constint num =10000000;intmain(){int i, x;//freopen是将stdin重定向到文件 //意思是scanf可以文件中读取数据 freopen("data.txt","r",stdin); clock_t t1, t2; t1 =clock();for(i =0; i < num; i++){scanf("%d",&x);} t2 =clock(); cout <<"Runtime of scanf: "<< t2 - t1 <<" ms"<< endl;return0;}

运行演示结果:

在这里插入图片描述
#include<iostream>#include<ctime>#include<cstdio>usingnamespace std;constint num =10000000;intmain(){//freopen是将stdin重定向到文件 //意思是cin可以文件中读取数据 freopen("data.txt","r",stdin);int i, x; clock_t t1, t2; t1 =clock();for(i =0; i < num; i++){ cin >> x;} t2 =clock(); cout <<"Runtime of cin: "<< t2 - t1 <<" ms"<< endl;return0;}

运行演示结果:

在这里插入图片描述

如果对 cin 进行优化呢?

#include<iostream>#include<ctime>#include<cstdio>usingnamespace std;constint num =10000000;intmain(){ ios::sync_with_stdio(false);//取消给C语言输入输出缓冲区的同步  cin.tie(0);//取消了cin和cout的绑定 freopen("data.txt","r",stdin);int i, x; clock_t t1, t2; t1 =clock();for(i =0; i < num; i++){ cin >> x;} t2 =clock(); cout <<"Runtime of cin: "<< t2 - t1 <<" ms"<< endl;return0;}

运行演示结果:

在这里插入图片描述

所以未来我们在使用 scanf / printf 和 cin / cout 抉择的时候,如果要追求性能那就使用 scanf / printf,或者优化版的 cin / cout,如果不追求性能,直接使用 cin / cout 就行。

如果输入的数据量比较小(10^6 以内)的话,用 cin 和 cout 或 scanf 和 printf 都行;但是输入的数据量比较大(10^9 左右)的话,更推荐使用 scanf 和 printf,避免因为输入输出的开销,导致代码超时;在大多数场景下 printf / scanf 和 cin / cout 的使用根据个人习惯进行选择
即可。

这里我们讨论了 scanf/printf 和 cin/cout 性能的差异,我们知道 scanf/printf 是更快
的,其实有一些极端情况下,输入输出的规模非常大的时候,scanf 和 printf 也不能满足的时候,会使用快速读写的方式,这个后期会讲。


结语

掌握输入输出的底层机制和陷阱,才能写出健壮高效的代码。从缓冲策略到格式控制,每个细节都可能成为调试时的关键。

希望这篇文章能给各位朋友对 C/C++ 的输入输出有着不一样的理解。

同时愿诸君能一起共渡重重浪,终见缛彩遥分地,繁光远缀天



Read more

用 10% GPU 跑通万亿参数 RL!马骁腾拆解万亿参数大模型的后训练实战

用 10% GPU 跑通万亿参数 RL!马骁腾拆解万亿参数大模型的后训练实战

整理 | 梦依丹 出品 | ZEEKLOG(ID:ZEEKLOGnews) 左手是提示词的工程化约束,右手是 Context Learning 的自我进化。 在 OpenAI 新发布的《Prompt guidance for GPT-5.4》中,反复提到了 Prompt Contracts(提示词合约)。要求开发者像编写代码一样,严谨地定义 Agent 的输入边界、输出格式与工具调用逻辑,进而换取 AI 行为的确定性。 但在现实操作中,谁又能日复一日地去维护那些冗长、脆弱的“提示词代码”? 真正的 Agent,不应只靠阅读 Context Engineering,更应该具备 Context Learning 的能力。 为此,在 4 月 17-18

By Ne0inhk
当OpenClaw引爆全网,谁来解决企业AI Agent的“落地焦虑”?

当OpenClaw引爆全网,谁来解决企业AI Agent的“落地焦虑”?

2026 年 3 月,开源 AI Agent 框架 OpenClaw 在 GitHub 上的星标突破28万,并一度超越 React,成为 GitHub 最受关注的软件项目之一。短时间内,开发者利用它构建了大量实验性应用:从全栈开发辅助,到自动化营销脚本,再到桌面操作自动化,AI Agent 的能力边界正在迅速被拓展。 这股热潮也带动了另一个趋势——本地部署与算力硬件需求的快速增长。越来越多开发者尝试在个人设备或企业服务器上运行 Agent 系统,以获得更高的控制权和数据安全性。 从表面上看,AI Agent 似乎正从“概念验证”走向更广泛的开发实践。但在企业环境中,情况却没有想象中乐观。当企业负责人开始追问—— “它能直接解决我的业务问题吗?” 很多演示级产品仍难以给出令人满意的答案。 如何让 Agent 真正融入企业既有系统、适配复杂业务流程,正成为大模型产业落地必须跨越的一道门槛。 与此同时,中国不同城市的产业结构差异明显:互联网、

By Ne0inhk
二手平台出现OpenClaw卸载服务,299元可上门“帮卸”;2026年春招AI人才身价暴涨:平均月薪超6万;Meta辟谣亚历山大·王离职 | 极客头条

二手平台出现OpenClaw卸载服务,299元可上门“帮卸”;2026年春招AI人才身价暴涨:平均月薪超6万;Meta辟谣亚历山大·王离职 | 极客头条

「极客头条」—— 技术人员的新闻圈! ZEEKLOG 的读者朋友们好,「极客头条」来啦,快来看今天都有哪些值得我们技术人关注的重要新闻吧。(投稿或寻求报道:[email protected]) 整理 | 苏宓 出品 | ZEEKLOG(ID:ZEEKLOGnews) 一分钟速览新闻点! * 微信员工辟谣“小龙虾可自动发红包”:不要以讹传讹 * 蚂蚁集团启动春招,超 70% 为 AI 相关岗位 * 受贿 208 万!拼多多一员工被抓 * 2026 年春招 AI 人才身价暴涨: 平均月薪超 6 万元 * 二手平台出现 OpenClaw 上门卸载服务 * 权限太高,国家互联网应急中心发布 OpenClaw 安全应用的风险提示 * 字节豆包内测 AI 电商功能:无需跳转抖音,日活用户数超

By Ne0inhk
遭“美国政府封杀”后,Anthropic正式提起诉讼!

遭“美国政府封杀”后,Anthropic正式提起诉讼!

整理 | 苏宓 出品 | ZEEKLOG(ID:ZEEKLOGnews) 据路透社报道,当地时间周一,AI 初创公司 Anthropic 正式对美国国防部及特朗普政府提起诉讼,抗议五角大楼将其列为“国家安全供应链风险”主体的决定。 Anthropic 在向美国加州北区地方法院提交的诉讼文件中表示,这一认定“史无前例且非法”,已对公司造成“不可挽回的损害”。公司希望法院撤销该决定,并指示联邦机构停止执行相关认定。 划定 AI 应用红线,双方观点不一 正如我们此前报道,这场争端的核心在于 Anthropic 为其核心 AI 模型 Claude 设定的两条技术使用红线,与美国国防部的使用需求发生根本冲突。 此前,Anthropic 曾与五角大楼签署一份价值最高可达 2 亿美元的合作合同,Claude 也成为少数被纳入美国机密网络环境进行测试的 AI 系统之一。 对此,Anthropic 一直坚持两条底线: * Claude 等技术不得被用于对美国民众的大规模国内监控;

By Ne0inhk