【C语言】常见的scanf()接收异常及注意事项
问题简述
作者在写链表的创建时突然发现一个玄学问题,scanf接收的值从第二个开始总是有问题:
while(flag=='s'){
/*......*/
scanf("a=%c",&x);
/*......*/
scanf("b=%c",&flag);
/*......*/
}
x接收是没有问题的,但到flag这里还没有输入就直接跳出循环了。
原因分析
跳出循环可能有两个原因
- 不满足循环条件
- 程序崩溃
第一点说明是flag的值不是's';第二点程序直接崩溃结束的话后续代码不会执行,但实际上作者的后续代码是正常的。因此可以判断是其接收异常。
定位错误
监控该变量,发现该变量的值是'\n'即为换行符。
也就是说我们输入完第一个x之后的按下的换行被记录保存下来了。
作者去查了一下源码及相关资料,发现是其缓冲区的问题。实际上,我们输入的过程中除了内容之外还有一个换行符,都储存在缓冲区了。
scanf只是把缓存区的内容取走了,换行符却还在缓冲区中,因此下一次执行scanf时直接把换行符当输入取走了。
解决问题
我们需要在输入之后清除这个换行符,我们可以用getchar()这个函数,getchar会从缓冲区取走一个字符。
while(flag=='s'){
/*......*/
scanf("%c",&x);
getchar();//从缓存区取走一个字符
/*......*/
scanf("%c",&flag);
getchar();//从缓存区取走一个字符
/*......*/
}
到这里我们这个问题就解决了,为了以后少犯错误接下来我们进一步去探究这个问题。
scanf函数
int scanf(const char *format, ...);
format
:格式字符串,指定了输入数据的格式。...
:可变参数列表,对应于格式字符串中的每个格式说明符,需要提供相应类型的指针以存储输入的数据
可能很多朋友们不知道,scanf是有返回值的,scanf的返回值是读入成功的数据项数,既一个int值。
int num=scanf("%d %d",&a,&b);
- 如果我输入 1和2 则num的值为2
- 如果我输入 a和2 则num的值为1
- 如果我输入 a和b 则num的值为0
常见的格式说明符
%d | 读入有符号十进制整数 |
%f | 读入有符号浮点数 |
%c | 读入单个字符,后续不会加空字节 |
%s | 读入字符串,后续会加空字节,遇到空白字符完成读取 |
%p | 读入指针值 |
空白字符
指的是一些不可见字符,主要用于格式化文本,常见的空白字符有:
- 空格(' ')
- 水平制表符('\t')
- 换行符('\n')
- 垂直制表符('\v')
- 换页符('\f')
- 回车符('\r')
getchar函数
函数原型:int getchar(void)
这个函数的实际用法是接收一个字符,其返回值是ASCII表的码值。
因为其接收是从缓冲区中接收(scanf也是从缓冲区中接收),也常常被我们用来清除缓冲区残留的内容。
如果缓冲区没有内容,则和scanf类似,需要我们键盘键入,但重要的是其输入只能是一个字符
fflush(stdin)函数
清空缓冲区函数。
该函数可以直接清空缓冲区。但其不够稳定所以用的较少。我们可以看到其输入是stdin,表示输入流,因为该行为没有标准定义,在不同编译器或者不同平台上可能出现不同行为。
作者在DEV C++中使用是没有问题的,但不同编译器不确定,因此需要注意。
如果要清空缓冲区可以用scanf或getchar去循环读取并丢弃字符,例如:
void clear_input_buffer() {
int c;
while ((c = getchar()) != '\n' && c != EOF);
}
【注意】上述的scanf和getchar函数的返回值还有一个EOF,表示文件结束符,其值一般为-1.