C语言——使用scanf()函数遇到的一个问题

一、问题由来

昨天在计蒜客题库刷题,遇到这样一道题(T1088画矩形),题目如下:

C语言——使用scanf()函数遇到的一个问题

功能实现的代码写完后运行,按要求输入测试数据后,不能得到正确的输出。但是检查代码发现语法和算法都没有问题,一番折腾后才发现,问题出在输入函数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,如下图所示。

C语言——使用scanf()函数遇到的一个问题

三、问题分析

对%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语言——使用scanf()函数遇到的一个问题

六、参考文章

C语言中文网:C语言scanf函数用法完全攻略


C语言——使用scanf()函数遇到的一个问题