前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >原创 | 函数 scanf 前世今生

原创 | 函数 scanf 前世今生

作者头像
用户2617681
发布2019-08-08 15:01:09
7780
发布2019-08-08 15:01:09
举报
文章被收录于专栏:秘籍酷秘籍酷
C语言初学者,最常用的函数当属 printf() 和 scanf() ,前者无用多言,毕竟鼎鼎大名的 HelloWorld 也要仰仗它出手,printf()函数只管将数据输出至屏幕,基本没有什么出错的机会,而后者 scanf() 则隐晦许多,甚至有些自称编程老鸟也未必深谙其内涵,这篇小文,作为初级出门装,建议初学者们第一时间买上。

先来一段白痴式代码(idiot.c),由易入难,以体现我一贯的思维严谨性:

int age; scanf("%d", &age); printf("哇!您 %d 岁了!\n", age);

上述代码作何解读? 简单,就是让你从标准输入设备(也就是键盘),敲入一个十进制整数,然后放进变量 age 之中。然后做一惊一乍状爆出你的年龄。

既然本文面向C语言初学者,我也不怕做个长舌妇,把话说得更加完(luo)满(suo)一点,来提几个找抽的问题:

① 为什么是从键盘输入?

② 为什么是十进制整数?

③ 如果我就是要胡乱输入,你奈我何?(划重点)

不急,来一拳拳抡死这个智障

① 为什么是从键盘输入? 因为 scanf() 函数默认就是从键盘读取数据呀!好吧,这个回答可能会觉得索然无味,但如果我们认识 scanf() 的其他几个同门亲兄弟的话,可能感觉会有点不一样,他们是:

sscanf(); // 专门从某块内存读取数据 fscanf(); // 专门从某个文件读取数据

因此你现在知道,scanf() 只是家族众多兄弟中的一员,大家各有所好而已,没错,scanf() 就是专门从键盘读取数据的那个家伙。

② 为什么是十进制整数?因为代码中的 %d 就是 decimal 的首字母,这表明此时 scanf() 就是希望你输入一个十进制整数,这个 %d 就是所谓的格式控制符。那你会问了,如果希望输入别的什么进制的整数呢?或者浮点数、字符串呢?你猜到了,那将会有不同的控制符来表示,比如:

有了上表,可见我没骗你,%d 真是输入十进制整数的意思!

③ 如果我就是要胡乱输入,你奈我何?这个问题是本文要讨论的重点,先来看看一个很皮的家伙,是怎么戏弄上面这段程序的:

当某人输入二百五的时候,这段程序很老实地说他已经250岁了,虽然看起来无可指责,毕竟年龄是他自己输入进去的,但我们总会觉得这个程序缺少一点脑筋,正常来讲它应该要把人的年龄限制在一个合理的范围,比如:1 - 100岁之间。当人类输入一个不合理的年龄的时候,程序应该要能指出人类的愚蠢,很可惜我们的白痴程序没能做到这一点。

解决这个BUG比较简单,只要做个数值判断就可以了:

int age; scanf("%d", &age); if( age < 1 || age > 100) printf("您确定您不是妖怪?\n"); else printf("哇!您 %d 岁了!\n", age);

再来看更离谱的错误:

当某个人类输入一个完全不是年龄的东西的时候,程序彻底傻X了,输出了一个完全不合理的非法年龄,你可以理解为:程序陷入了迷乱

接下来,我们要改造一下程序,使之具备一定的智能。但在此之前,需要对 scanf() 的来龙去脉理清头绪。

首先,当我们说函数 scanf() 是从键盘获取数据的时候,我们要承认这个说法是不严谨的,严格讲,scanf() 只是从键盘对应的文件的缓冲区中读取数据,而无法直接读取键盘敲入的数据,可以想象,键盘到 scanf() 中间有一段路程要求,要完美讲清楚这个过程显然要画出图来,以示诚意,是时候展现我的绘画才艺了,请欣赏:

对上图做点解释:

① 手指敲击键盘时,数据由键盘的驱动程序读取,并被保存在驱动程序中,此时跟scanf()没有半毛钱关系。

② 输完了并敲击回车键后,驱动程序将数据送往缓冲区,并通知 scanf() 来搬运数据。

③ scanf() 带着参数 %d 来到缓冲区,跟缓冲区中的数据格式对了对眼神,如果发现格式没错,那就搬走,放到你指定的 age 里面,如果格式不对,那 scanf() 将一走了之,不干任何事情。

④ 如果scanf() 成功搬运了一个数据,那就返回1,如果成功搬运了两个数据,那就返回2,如果没跟任何数据对上眼神,就返回0。

有了以上的工作流程,我们就可以改进上面的 idiot.c ,改成 regular.c。我们可以通过判断 scanf() 的返回值,来知道它究竟搬运了数据没有:

int age; if ( scanf("%d", &age) != 1) printf("叫你输入整数,别整些没用的!\n"); else if( age < 1 || age > 100) printf("您确定您不是妖怪?\n"); else printf("哇!您 %d 岁了!\n", age);

来试试效果:

现在程序有点像正常人了,但这还不够,还有个不大不小的BUG,请看:

输入 23abc 本来应该是一个不正经的年龄,但是程序没报错,而是直接将前面的23读取并汇报了年龄。换句话讲,当输入 23abc 的时候,scanf() 是正常工作的,它返回了 1,正常拿到了整数数据并搬到了 age 里,只不过留下了未能匹配格式的 abc 在缓冲区中没有收拾,造成以上BUG。

这个问题的解决,就不能简单地判断 scanf() 的返回值,而是在他返回正常的数据个数之后,还要判断缓冲区中是否还残留有非法格式的数据,这个怎么判断呢?这就要用另一个函数了,就是它:

getchar();

这个函数的作用,就是读取缓冲区中的一个字符。你想,如果我们输入的是正常的年龄,比如 23 ,那么当 scanf() 把 23 搬走之后,缓冲区中必然留下的是一个回车键,即 "\n",否则,缓冲区中必然会留下其他的字符,根据这个思路立刻修改程序,变身为 smart.c :

int age; if ( scanf("%d", &age) != 1 ) printf("叫你输入整数,别整些没用的!\n"); else if ( getchar() != '\n' ) printf("数字后面别拖个尾巴,OK?\n"); else if( age < 1 || age > 100) printf("您确定您不是妖怪?\n"); else printf("哇!您 %d 岁了!\n", age);

最后测试一下程序:

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-07-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 秘籍酷 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档