拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

两道C入门题:scanf多组输入怎么判断,最大最小值怎么初始化

两道C入门题:scanf多组输入怎么判断,最大最小值怎么初始化

这两道题来自我学习 C 时做的牛客入门练习:BC49 判断两个数的大小、BC95 最高分与最低分之差。题目本身不复杂,但多组输入和初始化值得单独说清楚。

1. 判断两个数:我的原解法为什么是 == 2

每组读两个整数,输出它们的大小关系。我的原解法核心保持不变:

#include<stdio.h>intmain(void){inta,b;while(scanf("%d %d",&a,&b)==2){charrelation;if(a>b)relation='>';elseif(a==b)relation='=';elserelation='<';printf("%d%c%d\n",a,relation,b);}return0;}

测试输入:

3 2 2 2 -1 5

输出:

3>2 2=2 -1<5

我的出发点是“一组有两个数”,因此循环应当在两个转换都成功赋值时进入。scanf的返回值不是读到的字符数,也不是简单的成功或失败开关。

四种输入,四种情况

输入情况%d %d的返回值能否处理完整一组
3 22能
3 x1不能,第二次转换匹配失败
x0不能,第一次转换就匹配失败
第一次赋值之前就遇到输入结束EOF不能

如果先读到一个整数再遇到文件结束,返回 1,不是 EOF。EOF 是一个负的整型常量,本机值为 -1,但代码不该把某个实现值当成所有实现的规定。

为什么 != EOF 和按位取反容易误导

while (scanf("%d", &n) != EOF)在输入x时可能继续进入循环:返回 0,不等于 EOF。错误字符没有被这个%d转换消耗,再次尝试还可能失败。如果没安排恢复逻辑,就会反复卡在同一个位置。单个整数也应该用== 1检查一次完整转换。

我原来写的“~2是 0”是错误的。本机 GCC 环境中:

~2 = -3 ~0 = -1 ~(-1) = 0

~是按位取反,不是“判断输入成功”。即使在常见的 EOF=-1 环境,~scanf(...)仍然不能识别返回 0 的匹配失败,也不能保证两个数都读到了。

用有界字符串输入复现,不把测试程序写成死循环

#include<stdio.h>intmain(void){constchar*cases[]={"3 2","3 x","x",""};for(size_ti=0;i<sizeofcases/sizeofcases[0];++i){inta=0,b=0;intresult=sscanf(cases[i],"%d %d",&a,&b);if(result==EOF)printf("EOF\n");elseprintf("matched:%d\n",result);}return0;}

输出:

matched:2 matched:1 matched:0 EOF

这个实验验证的是转换和返回值,不是完整的交互式输入恢复器。评测题保证合法输入时,遇到不完整组直接退出就够用;真实交互程序还要定义怎么报告错误、清理输入并重试。scanf也不能自动解决超出整数类型范围的外部输入问题,严肃文本解析可以进一步学习fgets与strtol。

2. 成绩差:我为什么初始化成 max=0、min=100

原题成绩范围是 0 到 100。我的想法是:先给最大值一个不大于任何合法成绩的初值,最小值给一个不小于任何合法成绩的初值。

max = 0 min = 100

这个思路在题目范围内成立,不需要因为代码简单就否定它。问题在于:如果以后遇到负数、温度或任意整数,这两个初值不能直接照搬。

原程序还先保存数组,再分别扫描最大值和最小值。它能做这件事,但没有必要存下所有成绩。每读一个数,就可以同时更新两项统计量。

更通用的写法:用第一个数据初始化

#include<stdio.h>intmain(void){intn,value;if(scanf("%d",&n)!=1||n<=0)return1;if(scanf("%d",&value)!=1)return1;intmin_value=value,max_value=value;for(inti=1;i<n;++i){if(scanf("%d",&value)!=1)return1;if(value<min_value)min_value=value;if(value>max_value)max_value=value;}printf("%lld\n",(longlong)max_value-(longlong)min_value);return0;}

这里先保证有第一个值,再让最大值和最小值都等于它。原题的差值最多 100,int足够;为了在本机常见的 32 位 int 环境扩展测试,我在减法之前转成long long。这不代表所有 C 实现的任意 int 差值都必然能装进 long long。

输入5 60 80 100 0 75,输出100;输入1 88,输出0;扩展测试3 -8 -2 -5,输出6。后一个不属于成绩题范围,只用来说明初始化方式可复用。

为什么这次能省掉数组

读完前 i 个值时,我维护的两个量始终分别是这 i 个值的最小值和最大值。第 i+1 个值只可能扩大边界,不需要回看旧数据。因此时间 O(n),额外空间 O(1)。

这不是“数组不好”,而是输出只依赖两个统计量。如果下一步还要排序或找中位数,需求就变了。

原代码里还有两个小地方:

  • int arr[10000] = {-1};只有第一个元素是 -1,其余是零,不是全部 -1。
  • scanf("%d ", &value)的尾部空白可能让交互输入继续等待后续非空白字符;这里只需要"%d"。

3. 验证比“看着对”多一步

本次比较程序测试了大于、等于、小于、负数,以及错误首项、错误第二项和空输入;成绩程序测试了单值、重复值、0/100 边界和扩展负数,并与独立排序结果对照。

两题真正可带走的不是两段答案,而是两个习惯:一组需要几次赋值,就检查几次;统计初值依赖题目范围,想复用就把这个前提拿掉。

返回列表