首页
社区
课程
招聘
TC中scanf的BUG及解决
发表于: 2005-6-8 18:10 10889

TC中scanf的BUG及解决

2005-6-8 18:10
10889
高中搞NOI的时候用的是TC,深受一个问题困扰――scanf()函数为什么经常在取过一次用户输入后,就发疯似的不再停下来了。为这个问题,偶还专门请教过某高手,得到的不过是含糊其辞的托词。依稀记得这位高手还曾经教育过偶说win98刚启动起来的时候,不要随便乱动鼠标,否则鼠标的驱动程序会丢的 。从此之后,偶牢记奉行,98启动时再也不敢恣意的晃动鼠标了

当年的水平也就处于写写C代码,做做NOI题目(还经常做不出来)的水平上,更别提自己调试分析了。后来久而久之的就忘记掉这个问题了。不过后来时常在噩梦中与之相会,爽不堪言。

今天看到某一网站,居然无意中发现了问题的解决方法(http://www.china-askpro.com/msg14/qa80.shtml),一解多年来心头之埂。现把全文摘录如下:

<><> sunny:  
    我初学C程序,所以提的问题很浅,希望您不要见笑。我自己编了一个程序,但运行的结果与我预期的不一样。
    # include
    main()
    {static int a[2][3]={{1,3,4},{7,9,6}};
    int i,j,k;
    for(k=1;k<=2;k++)
    {printf("Please input num:");
    scanf("%d %d",&i,&j);
    if(i<2&&j<3)
    printf("num=%d\n",a[i][j]);
    else printf("Input is error,\n");
    }
    printf("programm is complete.\n");
    }
    我想将第7行改为
    scanf("i=%d j=%d",&i,&j);
    则程序运行结果变成
    please input num:i=1 j=2
    num=6
    num=6(我原本希望能重复第一行再让我输入)
    Programm is complete.
    为什么第二次不能输入?
回答:

    我使用Turbo C 2.0证实存在你说的问题。象scanf("i=%d j=%d",&i,&j);这样的输入方式比较特别,TC 2.0显然在第一次输入后没有象正常情况一样清楚输入缓冲区,这样第二次执行scanf时,程序并没有让你输入而是直接读入上次输入的结果。如果你一定要这么做,应该在scanf之前加上:
    fflush(stdin);
    这样清楚掉键盘缓冲区。

嘿嘿,可以安心的睡大觉了。

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (18)
雪    币: 603
活跃值: (617)
能力值: ( LV12,RANK:660 )
在线值:
发帖
回帖
粉丝
2
在我困惑时,这个fflush是一个大牛告诉我的,不过在不知道的时候还真是一头雾水~
2005-6-8 18:23
0
雪    币: 2319
活跃值: (565)
能力值: (RANK:300 )
在线值:
发帖
回帖
粉丝
3
最初由 monkeycz 发布
高中搞NOI的时候用的是TC,深受一个问题困扰――scanf()函数为什么经常在取过一次用户输入后,就发疯似的不再停下来了。为这个问题,偶还专门请教过某高手,得到的不过是含糊其辞的托词。依稀记得这位高手还曾经教育过偶说win98刚启动起来的时候,不要随便乱动鼠标,否则鼠标的驱动程序会丢的 。从此之后,偶牢记奉行,98启动时再也不敢恣意的晃动鼠标了

当年的水平也就处于写写C代码,做做NOI题目(还经常做不出来)的水平上,更别提自己调试分析了。后来久而久之的就忘记掉这个问题了。不过后来时常在噩梦中与之相会,爽不堪言。

今天看到某一网站,居然无意中发现了问题的解决方法(http://www.china-askpro.com/msg14/qa80.shtml),一解多年来心头之埂。现把全文摘录如下:
........


根据 ANSI C标准,和一些传统 C 的经验

1.        C 语言的fflush 只是用作清除 output stream 之用。 对于使用 fflush 在 stdin 这个动作,是没有定义的行为  

( ref: ISO/IEC 9899-1999  7.19.5.2 , ANSI Sec. 4.9.5.2 )

2.        这个问题并不是 Turbo C 2.0 的 bug,这是标准 scanf 的正常行为。

Scanf 并不清理使用者输入的 newline character (0xa) ,在连续的 scanf 中如果参数字符串的第一个字是 % 参数  (for example: scanf(“%d”); ),它将会顺利地把  <last newline char><string> 读取,并且把 newline char 视作 white space,把它忽略。因此,在这个帖的代码 (修改前) 是可以正常运作。这是传统的 scanf 运用方法。

修改后的代码,  scanf("i=%d j=%d",&i,&j);  ,问题出现在参数字符串的开头是 ‘i=’ ,使 scanf 遇到最后一次 newline character 的时候便进行辨认,结果提前结束,返回错误。 这段程序的 i 和 j 不变,所以情形像 scanf 把上一次的数据读取一样,做成这个错误。

标准 scanf 本身的设计存在很多毛病,所以普遍建议是,不要采用 scanf,或是在 scanf 后作出完善的检查和处理。

3.        在这个情况,由于 fflush 不应该使用在 stdin 上,所以我们使用其它方法来解决,例如在 scanf 后面,加上一行  getchar(); ,把没有清理的 newline char 消除

    printf("Please input num:");
    scanf("i=%d j=%d",&i,&j);
    getchar();  // clear newline
2005-6-9 11:41
0
雪    币: 1223
活跃值: (469)
能力值: (RANK:460 )
在线值:
发帖
回帖
粉丝
4

原来如此。
2005-6-9 12:39
0
雪    币: 761
活跃值: (3562)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
5
scanf("\ni=%d,j=%d",&i,&j);
也一样
2005-6-13 07:49
0
雪    币: 2319
活跃值: (565)
能力值: (RANK:300 )
在线值:
发帖
回帖
粉丝
6
不可以

第一次scanf 的时侯 stdin 空白,没有 \n

我把你这句编译试验了,确实出错
2005-6-13 08:14
0
雪    币: 761
活跃值: (3562)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
7
你的编译平台?
程序完整代码?

我在tc 2.01和vc 6.0 intel CPP8.01上正常编译
执行没有任何问题
2005-6-13 08:34
0
雪    币: 2319
活跃值: (565)
能力值: (RANK:300 )
在线值:
发帖
回帖
粉丝
8
最初由 meilin 发布
你的编译平台?
程序完整代码?

我在tc 2.01和vc 6.0 intel CPP8.01上正常编译
执行没有任何问题


当然是使用原作者的代码,加上你这句

#include "stdio.h"

int        main()
{
        static int a[2][3]={{1,3,4},{7,9,6}};

        int        i,j,k;

        for(k=1;k<=2;k++)
        {
                printf("Please input num:");

                scanf("\ni=%d,j=%d",&i,&j);

                if(i<2 && j<3)
                        printf("num=%d\n",a[i][j]);       

                else
                        printf("Input is error,\n");
        }

        printf("programm is        complete.\n");
}

运行会出错

你试输入  i=0 j=1
2005-6-13 09:22
0
雪    币: 761
活跃值: (3562)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
9
你应该输入i=0,j=1.
2005-6-13 09:43
0
雪    币: 2319
活跃值: (565)
能力值: (RANK:300 )
在线值:
发帖
回帖
粉丝
10
原来把空格改了,所以出错

其实不把空格改成逗点,也可以顺利运作

scanf("\ni=%d j=%d",&i,&j);

看来只要把开头的第一个字玩设成 white space,便可以把 scanf 的参数扫瞄骗过

你试试这句

scanf(" i=%d j=%d",&i,&j);

i 前只用一个空格,也顺利运行了
2005-6-13 10:06
0
雪    币: 761
活跃值: (3562)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
11
恩,就是给它一个它认为忽略的字符就ok
不过这样做已经在玩火.最好是老实
的在后面加getchar()
会读起来明朗点
2005-6-13 10:12
0
雪    币: 2319
活跃值: (565)
能力值: (RANK:300 )
在线值:
发帖
回帖
粉丝
12
外国的 C 老手建议,在写一些需要安全性的程序时,使用 scanf 时要防止输入内存的 overflow 等等的问题,所以不再使用 scanf,使用较安全的 fgets + sscanf 代替

char buf[20];   // 指明我们的容许输入内存

…..

                fgets( buf, 20, stdin );      //  限制只读取 20 位
                sscanf( buf, "i=%d j=%d",&i,&j);   // 把读取后的 buffer 进行扫瞄

这种手法可以保障安全,也可以同时解决 stdin 的 \n问题
2005-6-13 10:25
0
雪    币: 212
活跃值: (40)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
13
这个问题,以前也遇到过;
记得SCANF中的输入同参数是一一对应的关系就不会错了
2005-10-1 16:01
0
雪    币: 343
活跃值: (611)
能力值: ( LV9,RANK:810 )
在线值:
发帖
回帖
粉丝
14
早在做一个dos程序时也遇到过这样的问题。
2005-10-1 16:27
0
雪    币: 234
活跃值: (370)
能力值: ( LV9,RANK:530 )
在线值:
发帖
回帖
粉丝
15
riijj是C高手啊
2005-10-2 16:30
0
雪    币: 233
活跃值: (130)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
16
现在还看不懂,希望下个月能看懂
2005-10-2 16:40
0
雪    币: 122
活跃值: (45)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
17
不一定只有TC平台有这样的问题,换到VC平台也是这样的.
scanf("\ni=%d j=%d",&i,&j);中的"i="和"j="是不是用的有点多余
这种输入格式不是太常用吧
2005-10-2 18:21
0
雪    币: 370
活跃值: (15)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
18
点点滴滴都是时间+汗水凝聚而成呀
记下了!
2005-10-2 20:57
0
雪    币: 234
活跃值: (104)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
记得哪个网站上说,scanf函数应该禁止使用。
好像标准C库函数中,有不少函数是bug函数。
2005-10-4 14:31
0
游客
登录 | 注册 方可回帖
返回
//