事情是这样的,在今天的快乐刷题中,我遇见了一个题目:
试题以及部分检查点
ummm,看上去很简单:int的范围是 -2^31 到 2^31 - 1,所以直接定义两个整形变量,然后用scanf函数进行值传递,最后使用多个并列if似就可以解决这个问题。
于是我满怀信心给出了第一版答案:
#include <stdio.h>
int main() {
int a, b;
scanf("%d%d", &a, &b);
if (a > b) printf("%d>%d\n", a, b);
if (a == b) printf("%d=%d\n", a, b);
if (a < b) printf("%d<%d\n", a, b);
return 0;
}
结果却出错了。
第一次错误
观察了一下,原来是需要添加一个循环。
此时我还没有意识到事情的严重性,高高兴兴的添加了一个for()循环结构,然后在printf()中添加转义字符”\n“进行换行。
#include <stdio.h>
int main() {
for (int i = 0; i < 3; i++) {
int a, b;
scanf("%d%d", &a, &b);
if (a > b) printf("%d>%d\n", a, b);
if (a == b) printf("%d=%d\n", a, b);
if (a < b) printf("%d<%d\n", a, b);
}
return 0;
}
很好,第二次提交。
结果却让我不寒而栗:
第二次错误
啊?不是?为什么会这样?
输入的次数不确定?
为什么scanf在没有接受到新的值的时候还会执行下一步?
啊?这是什么题?我没见过啊?
要解决这个问题,我们需要一个模块判断scanf函数是否成功接受到数值。
那么,这就要依靠scanf本身的性质了。
通过复习scanf的性质,我发现了一个突破口:
scanf
函数的返回值是成功匹配并赋值的输入项的数量。
如果输入失败或者遇到文件结束(EOF),返回值将小于预期的输入项数量。
很好,这就是我们所需要的!
基于这个性质,我编写了一个while循环
while(scanf("%d%d", &a, &b) == 2){}
这个循环首先会调用scanf函数,然后对scanf的返回值进行值判断,当scanf成功接收了2个参数时,判断的值就为真,循环执行。
我将代码整理出来,再次上传,果然成功了。
#include <stdio.h>
int main() {
int a = 0, b = 0;
while(scanf("%d%d", &a, &b) == 2) {
if (a > b) printf("%d>%d\n", a, b);
if (a == b) printf("%d=%d\n", a, b);
if (a < b) printf("%d<%d\n", a, b);
}
return 0;
}
那么,这段代码还有没有进一步优化的空间呢?
当然有,你会发现,三个if并列的结构会导致程序至少执行3次判断。
如果我们使用if-else if进行优化,那么程序执行判断的次数就变为1-3次。
#include <stdio.h>
int main() {
int a = 0, b = 0;
while(scanf("%d%d", &a, &b) == 2) {
if (a > b) printf("%d>%d\n", a, b);
else if (a < b) printf("%d<%d\n", a, b);
else if (a == b) printf("%d=%d\n", a, b);
}
return 0;
}
可以看到,运行时间和占用内存都有明显的优化。
scanf
函数的返回值是成功匹配并赋值的输入项的数量。如果输入失败或者遇到文件结束(EOF),返回值将小于预期的输入项数量。如果遇到文件结束符(EOF)或者读取出错,返回值将是 EOF
(通常是 -1)。
检查用户输入是否符合预期的格式。如果返回值与预期的输入项数量不符,说明输入可能不正确或不完整。
当scanf返回EOF或者一个负值时,可以进行特定的错误处理,比如提示用户重新输入或者结束程序。
在处理动态数量的输入时,scanf的返回值可以用来确定实际读取了多少个输入项,从而进行相应的处理。
在将输入的数据存储到数组或其他数据结构之前,可以根据scanf的返回值来确定需要存储的数据项数量。有助于避免数组越界或者未初始化的内存访问。