Blog Of Leung

personal blog & work notes

View project onGitHub
 

C语言中整数与字符串交替输入问题

这两天在鼓捣代码的时候遇到这样一个问题,接收键盘输入的时候,发现gets函数有时候会直接失效,问题是我已经在输入之后加上了fflush(stdin)语句来清除输入缓存区了,gets函数不应该会出问题才对呀。折腾了一阵,发现是fflush(stdin)在gcc下的问题,完了之后立马写笔记。。。当然,原代码不方便贴出来,所以写了个test程序来验证这个问题,嗯,以后博客上的代码就这么干。。先把代码贴出来。

#include <stdio.h>
#include <stdlib.h>
/*
for gets and scanf function test only
*/
struct student_info
{
    char no[10];
    char name[20];
    char sex;
    int age;
    char address[25];
};
typedef struct student_info STUDENT;

int main (void)
{
    int i, num;
    STUDENT *pstudent;

    printf ("input the number of students: ");
    scanf ("%d", &num);
    fflush (stdin);

    pstudent = (STUDENT *)malloc (num * sizeof(STUDENT));
    if (pstudent == NULL)
    {
        printf ("memory error\n");
        return;
    }

    for (i = 0; i < num; i++)
    {
        printf ("student %d no= ", i+1);
        gets (pstudent[i].no);
        fflush (stdin);

        printf ("student %d name= ", i+1);
        gets (pstudent[i].name);
        fflush (stdin);

        printf ("student %d sex= ", i+1);
        scanf ("%c", &pstudent[i].sex);
        fflush (stdin);

        printf ("student %d age= ", i+1);
        scanf ("%d", &pstudent[i].age);
        fflush (stdin);

        printf ("student %d address= ", i+1);
        gets (pstudent[i].address);
        fflush (stdin);
    }

    printf ("\n");
    printf ("%15s%15s%6s%5s%20s\n", "no", "name", "sex", "age", "address");
    for (i = 0; i < num; i++)
        printf ("%15s%15s%6c%5d%20s\n", pstudent[i].no, pstudent[i].name, pstudent[i].sex, pstudent[i].age, pstudent[i].address);
    free (pstudent);
}

这段代码中,需要注意的是main函数中的第一个for循环中的gets和scanf输入函数,前者常用来接受字符串输入,后者是标准输入函数。这段代码如果是用vc编译,那么是可以正常运行的,但是如果是用gcc编译,也可以编译通过,但是运行的时候就会出问题了。问题的表现是gets函数有时会工作不正常,不接收输入就直接过去了。

问题分析

在写这个输入功能的时候,我们已经知道scanf函数和gets函数是有自己的特性的,gets函数中换行是输入结束的标志,而scanf函数中空格或者换行都是输入结束的标志。程序一开始使用了scanf输入了一个num,按回车结束输入,然后进入到for循环时,又使用了gets函数,希望接收一个字符串输入,但是scanf不会保存输入num时的回车键,所以回车键符号'\n'就留在了输入缓存区,等到gets函数一执行,输入缓存区里面的'\n'会首先被gets函数获取,gets函数收到'\n'后立马认为输入已经结束了,所以才会出现这种不接收输入直接跳过gets函数的现象。所以为了避免这个问题,在scanf输入结束之后,我就加上了fflush函数,来清空输入缓存区。 而这个问题的症结在于,fflush(stdin)语句在gcc编译的情况下失效了。从各种查询的结果看来,貌似fflush(stdin)这种用法并不是被每一种编译器支持的,像上面的程序如果是在VC上面跑的话是没问题的,但是gcc下就没戏了。看来以后为了保险,要把这种用法抛弃掉才行。

既然问题的症结找到了,那么解决它也是不难的。既然判断scanf输入之后,回车键会留在缓存区,那么我们就利用这个回车键做个条件判断,如果gets函数接收到的字符串为空的话,就再接收一次就好了。代码上可以先把第一个scanf函数后面的fflush(stdin)删掉,然后for循环里面修改一下:

for (i = 0; i < num; i++)
{
    printf ("student %d no= ", i+1);
    gets (pstudent[i].no);
    if (pstudent[i].no[0] == '\0')
    gets (pstudent[i].no);

    printf ("student %d name= ", i+1);
    gets (pstudent[i].name);

    printf ("student %d sex= ", i+1);
    scanf ("%c", &pstudent[i].sex);

    printf ("student %d age= ", i+1);
    scanf ("%d", &pstudent[i].age);

    printf ("student %d address= ", i+1);
    gets (pstudent[i].address);
    if (pstudent[i].address[0] == '\0')
    gets (pstudent[i].address);
}

这样代码就可以正常运行了,不管是用什么编译器进行编译,这里面需要注意的是我们进行条件判断的时候并不是直接跟换行符进行比较,因为gets函数是接收到换行符之后认为输入已经结束,而实际上这时候的字符串是空的,空字符串的第一位那就是字符串的结束标志'\0'了,所以gets函数中要比较的是'\0'。

然后在这个过程中,顺便总结了一下几种输入的特点:

没问题的输入方式

  • scanf连续输入数字是没问题的
  • scanf连续输入字符串是没问题的
  • gets连续输入字符串是没问题的

有问题的输入方式

  • scanf输入数字然后gets输入字符串会有问题(这个上面已解决)
  • scanf连续输入字符会有问题
  • scanf输入数字然后再scanf输入字符会有问题

解决方法

scanf输入数字然后gets输入字符串的问题,上面已解决,就是gets之后判断字符串首位是否就是结束位,如果是,再接收一次输入

scanf连续输入字符的问题,以及scanf输入数字然后再scanf输入字符的问题,还是回车键引起的,解决的方法类似上面,但是区别在于,这里要判断的就是换行符本身了。

//scanf连续输入字符    
printf ("student %d no1= ", i+1);
scanf ("%c", &ch);
printf ("student %d no2= ", i+1);
scanf ("%c", &ch);
if (ch == '\n')
scanf ("%c", &ch);

//scanf输入数字然后再scanf输入字符
printf ("input the number of students: ");
scanf ("%d", &num);
printf ("student %d no1= ", i+1);
scanf ("%c", &ch);
if (ch == '\n')
scanf ("%c", &ch);

不过,gets这个函数貌似年代比较久远了,而且仅仅只有一个参数,毫无限制,所以这个函数也太危险了。避免使用或者才是最好的解决方法,应该用fgets函数替代它。

Author:leung

19 Feb 2014

← Home

comments powered by Disqus