C语言——使用scanf()函数遇到的一个问题
一、问题由来
昨天在计蒜客题库刷题,遇到这样一道题(T1088画矩形),题目如下:
功能实现的代码写完后运行,按要求输入测试数据后,不能得到正确的输出。但是检查代码发现语法和算法都没有问题,一番折腾后才发现,问题出在输入函数scanf()上。
二、问题描述
从题目的输入样例来看,一共要输入四个参数,每个参数之间用空格分开。一开始我使用的输入格式是:
int h, w, flag; //定义整型变量
char c; //定义字符变量
scanf("%d%d%c%d", &h, &w, &c, &flag);
因为我们知道,如果需要输入多个整型数据时,在输入控制符(双引号内的字符)中我们一般都可以将%d连在一起,中间不需要空格。所以这里我理所当然的认为%c也可以这样。但是事实并不然,在输入的下一行设置断点进入程序调试,输入“7空格7空格@空格0”后,可以看到整型变量h和w的数据接收正常,但是字符变量c和整型变量flag却没有像预期的那样接收字符@和整数1,如下图所示。
三、问题分析
对%d而言,空格、回车、Tab 键都是区分数据与数据的分隔符。当scanf进入缓冲区中取数据的时候,如果%d遇到空格、回车、Tab 键,那么它并不取用,而是跳过继续往后取后面的数据,直到取到“十进制整数”为止。对于被跳过和取出的数据,系统会将它从缓冲区中释放掉。未被跳过或取出的数据,系统会将它一直放在缓冲区中,直到下一个scanf来获取。
但对%c而言,任何数据都会被当作一个字符,不管是数字还是空格、回车、Tab 键它都会取回。
了解了这些后,我们来看一下按照上面的输入为什么会出问题。假设输入的是“7空格7空格@空格0”。对于第一个输入控制符%d,系统检测到第一个空格后,会将第一个空格前的7赋值给变量h,因为第二个输入控制符是%d,所以系统会把第一个空格从缓冲区释放掉。对于第二个输入控制符%d,系统检测到第二个空格后,会将第二个空格后前的7赋值给变量w,但是因为第三个输入控制符是%c,所以系统会将第二个空格赋值给字符变量c;对于第四个输入控制符%d,因为输入的字符@还在缓冲区内,但是%d遇到字母,它不会跳过也不会取用,而是直接从缓冲区跳出,因此flag的值是未初始化的一个值。这样整个输入的过程就结束了。
综上所述,在输入“7空格7空格@空格0”后,各个变量的值为:h = 7,w = 7,c = ‘ ’,flag = 未初始化的值。
四、解决办法
方法一:在第二个%d后加一个空格来对应输入中的第二个空格
scanf("%d%d %c%d", &h, &w, &c, &flag); //在第二个%d后加一个空格来对应输入中的第二个空格
方法二:用getchar()接收多余的空格
scanf("%d%d", &h, &w);
getchar(); //接收空格
scanf("%c%d", &c, &flag);
五、完整代码
#include<stdio.h>
//矩形打印函数
void fun(int h, int w, char c, int flag)
{
int i, j;
for (i = 0; i < h; i++) //控制行
{
for (j = 0; j < w; j++) //控制列
{
//如果第四个参数flag=1,
//或者第四个参数flag=0而且是第一行或者最后一行时
//每一行的每一列都输出字符c
if (flag == 1 || (flag == 0 && (i == 0 || i == h - 1)))
{
printf("%c", c);
}
//flag=0时的中间行(不包括第一行和最后一行)
else
{
//第一列和最后一列打印字符c
if (j == 0 || j == w - 1)
{
printf("%c", c);
}
//中间列打印空格
else
{
printf(" ");
}
}
//每行最后进行换行
if (j == w - 1)
{
printf("\n");
}
}
}
}
int main()
{
int h, w, flag;
char c;
//scanf("%d%d%c%d", &h, &w, &c, &flag); //不可以
//方式一
scanf("%d%d %c%d", &h, &w, &c, &flag); //可以
//方式二
scanf("%d%d", &h, &w);
getchar(); //接收空格
scanf("%c%d", &c, &flag);
fun(h, w, c, flag); //调用矩形打印函数
return 0;
}
六、参考文章
C语言中文网:C语言scanf函数用法完全攻略