1.背景
正常情况下,使用如下的scanf语句就可以向变量a循环输入一个十六进制的值。但是这种scanf的用法有问题吗?答案是有的。问题就在于输入一个非十六进制数的字符就会导致scanf获取值失败,程序进入死循环。
while(1)
{
int a;
scanf("%x",&a);
printf("%#x",a);
}
2.错误示例
错误示例的代码如下所示:
#include <stdio.h>
int main() {
int a=10;
while (1) {
scanf("%x", &a);
printf("%#x\n", a);
system("pause");
}
return 0;
}
如下所示,当程序执行后,不输入十六进制数,而输入“mmm”。这时发现程序会一直执行,而scanf就好像每次都会自动跳过一样,不再等待用户的输入了。
那么出现这种错误的原因是什么呢?首先看看对scanf的正确使用方式。
3.正确示例
如下代码演示了使用scanf进行数据输入时的正确用法。中间加入了一些打印信息,用于辅助理解输入缓冲区中的情况。
#include <stdio.h>
int main() {
int a=10;
int ret = 0;
char c = 'a';
printf("第一次输入:");
ret=scanf("%x", &a);
while (ret!=1) {
while ((c=getchar()) != '\n')
{
printf("缓冲区中的数据:%c\n", c);
}
printf("再次输入:");
ret=scanf("%x", &a);
}
printf("%#x\n", a);
system("pause");
return 0;
}
运行上面的代码,再次输入"mmm“等非十六进制字符时的现象如下:
可以看到,当输入“mmm”之后,scanf的返回值并非期望的1(实际是0,表示scanf操作失败,因为输入的数据不是十六进制数)。然后程序往下一个循环中,这个循环中调用了getchar(),为了展示当前输入缓冲区中的数据,使用一个字符获取了getchar()获取到的数据。可以看到,当scanf获取失败之后,其实输入缓冲区中的数据还是没有被取走,还是“mmm”。所以,这就解释了为啥错误示例中的scanf会一直执行失败,就好像直接跳过一样,原因就是输入缓冲区中的数据一直保持的“mmm”,导致scanf也一直返回错误。
4.总结
如果以后需要使用scanf进行循环输入数据,最好向正确代码中一样,使用一个循环用getchar()先把输入缓冲区的数据全部取走(直到获取到’\n’),这样就能确保获取到的是期望的数据。代码框架如下:
#include <stdio.h>
int main() {
int a=10;
int ret = 0;
printf("请输入:");
ret=scanf("%x", &a);
while (ret!=1) {
while (getchar() != '\n')
{
}
printf("请再次输入:");
ret=scanf("%x", &a);
}
printf("%#x\n", a);
system("pause");
return 0;
}