【算法竞赛】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

清华智谱开源7440亿参数的智能体GLM-5

简介 我们正式推出GLM-5,面向复杂系统工程与长周期智能体任务。规模化仍然是提升通用人工智能(AGI)智能效能的最重要途径之一。相比GLM-4.5,GLM-5将参数量从3550亿(激活320亿)扩展至7440亿(激活400亿),预训练数据从23万亿token增至28.5万亿token。GLM-5还集成了深度求索稀疏注意力机制(DSA),在保持长上下文能力的同时大幅降低部署成本。 强化学习旨在弥合预训练模型"达标"与"卓越"之间的鸿沟。然而由于RL训练效率问题,在大语言模型中规模化部署面临挑战。为此我们开发了slime——创新的异步RL基础设施,显著提升训练吞吐效率,支持更精细化的训练后迭代。得益于预训练与训练后的双重突破,GLM-5在各类学术基准测试中较GLM-4.7实现显著提升,在推理、编程和智能体任务领域达到全球开源模型顶尖水平,进一步缩小与前沿模型的差距。 基准测试 GLM-5GLM-4.7DeepSeek-V3.2Kimi K2.5Claude Opus 4.5Gemini 3 ProGPT-5.2

By Ne0inhk

idea中使用git

Git常用记录 * 一、Git基本配置 * 1.1 Git设置用户账号密码邮箱 * 1.2 Git生成秘钥 * 二、IDEA中使用Git * 1、配置idea * 2、克隆远程仓库 * 3、拉取代码、提交代码到本机仓库 * 4 、git项目回滚,回退版本 * 5、创建分支、合并分支 原文链接: IDEA 使用Git图文详解 一、Git基本配置 1.1 Git设置用户账号密码邮箱 查看信息: 查看用户名 :git config user.name 查看密码: git config user.password 查看邮箱:git config user.email 设置信息(–global

By Ne0inhk
20 万星开源神器 OpenClaw 全解析:程序员 + 视频博主双视角实战体验

20 万星开源神器 OpenClaw 全解析:程序员 + 视频博主双视角实战体验

2026 年初,AI 圈最大的黑马非OpenClaw莫属。这个从 Clawdbot、Moltbot 迭代而来的开源项目,在 GitHub 上星标狂飙至 21.7 万,成为现象级 AI Agent 框架。作为一名拥有 7 年大数据开发经验的程序员,同时也是正在转型视频剪辑的博主,我深度体验了这款工具近一个月,发现它不仅能解放开发者的双手,更能为内容创作带来革命性的效率提升。本文将从技术架构、核心功能、安装部署、双身份实战体验四个维度,带你全面解锁 OpenClaw 的奥秘。 一、核心定位与起源:从 “聊天 AI” 到 “能干活的数字员工” 1. 精准定义 一句话概括:OpenClaw 是本地可自托管、多渠道交互、具备强执行能力的开源 AI Agent 执行引擎。它打破了传统

By Ne0inhk
一人一虾一公司:OpenClaw登顶GitHub星标榜首

一人一虾一公司:OpenClaw登顶GitHub星标榜首

在AI代理赛道群雄逐鹿的2026年,一个由单人开发者发起的项目正以惊人速度重塑技术格局。从最初的 Clawbot,到象征“蜕壳成长”的 Moltbot,再到如今定名为 OpenClaw,这个项目在短短数月内经历了三次品牌蜕变,却始终聚焦于一个朴素而强大的目标:让AI不再只是说话,而是真正做事。 截至2026年3月2日,OpenClaw已在GitHub上斩获超过 24万多星标,成为开源社区的现象级存在。它采用醒目的“龙虾”图标,slogan直击人心——“The AI that actually does things”。该项目由知名独立开发者、PSPDFKit创始人 彼得·斯坦伯格(Peter Steinberger)开发,核心以 TypeScript 构建,可轻松部署在个人电脑、NAS或私有服务器上。 其影响力甚至溢出软件圈——因大量用户选择Mac Mini作为本地部署设备,这款小巧主机一度供不应求,销量显著攀升。更引人注目的是,OpenAI联合创始人 Andrej Karpathy 公开称赞OpenClaw为“

By Ne0inhk