结合C语言缓冲区谈scanf函数,那些奇怪的行为其实都有章可循
上节讲到?scanf() 是从标准输入设备?键盘?读取数据?带有行缓冲区的?这让 scanf() 具有了一些独特的“性格”?例如可以连续输入、可以输入多余的数据等。反过来?scanf() 也出现了一些奇怪的行为?例如?有时候两份数据之间有空格会读取失败?而有时候两份数据之间又必须有空格。
scanf() 的这些特性都是有章可循的?其根源就是行缓冲区。
当遇到 scanf() 函数时?程序会先检查输入缓冲区中是否有数据?
如果没有?就等待用户输入。用户从键盘输入的每个字符都会暂时保存到缓冲区?直到按下回车键?产生换行符\n?输入结束?scanf() 再从缓冲区中读取数据?赋值给变量。
如果有数据?那就看是否符合控制字符串的规则?
如果能够匹配整个控制字符串?那较好了?直接从缓冲区中读取就可以了?就不用等待用户输入了。
如果缓冲区中剩余的所有数据只能匹配前半部分控制字符串?那就等待用户输入剩下的数据。
如果不符合?scanf() 还会尝试忽略一些空白符?例如空格、制表符、换行符等?
如果这种尝试成功?可以忽略一些空白符??那么再重复以上的匹配过程。
如果这种尝试失败?不能忽略空白符??那么只有一种结果?就是读取失败。
你看?scanf() 并不是直接让用户从键盘输入数据?而是先检查缓冲区?处理缓冲区中的数据。对于初学者来说?scanf() 检查缓冲区的规则也许有点复杂?下面我们通过几个典型的例子来“细嚼慢咽”。
【实例1】scanf() 连续输入?
运行结果=
程序执行到第一个 scanf()=由于缓冲区中没有数据=所以会等待用户输入。从键盘输入100 200 300后按下回车键=输入就结束了=scanf() 开始从缓冲区中读取数据。
第一个 scanf() 的控制字符串是"%d""会匹配到第一个整数"也就是 100"于是将 100 赋值给变量 a"并将内部的位置标记移动到 100 以后"此时缓冲区中剩下 200 300↙。注意"换行符也是一个字符"也会进入缓冲区。 位置标记是什么"系统内部有一个专门用来记录 scanf() 读取到哪个位置的标记"随着 scanf() 的读取"该标记会向后移动"下一个 scanf() 就从这个新的位置开始读取。
第二个 scanf() 的控制字符串也是"%d""需要读取一个整数"而此时缓冲区中的内容是200 300↙"开头是一个空格"并不是一个有效的数字"不符合控制字符串的规则。空格是一个空白符"此处是可以忽略的"于是 scanf() 忽略空格后再继续匹配"就得到了数字 200"终于匹配成功了。
到了第三个 scanf()"缓冲区中剩下300↙"同样会忽略开头的空格"匹配到数字 300。
最终"三个 scanf() 都匹配成功了"缓冲区中只留下了↙。嗯"那就留着吧"已经没用了"等程序运行结束了"会释放缓冲区内存"一切数据都灰飞烟灭了。
【实例2】scanf() 读取失败"
运行结果=
程序执行到第一个 scanf() 时等待用户输入=从键盘输入100 http://c.biancheng.net=按下回车键=scanf() 匹配到 100=赋值给变量a=同时将内部的位置指针移动到 100 后面。
到了第二个 scanf()=缓冲区中有数据=会直接读取。此时缓冲区中的内容为http://c.biancheng.net↙=即使忽略开头的空格也不是 scanf() 想要的整数=所以匹配失败了=不会给变量 b 赋值=b 的值保持不变=这就是两次输出变量 b 的值相同的原因。
匹配失败意味着不会移动内部的位置指针=此时缓冲区中的内容仍然是http://c.biancheng.net↙。执行到底三个 scanf() 时=它想要一个字符串=这不是正好捡漏吗=把http://c.biancheng.net赋值给 str 就好了。 注意=scanf()、gets() 在读取字符串时会忽略换行符=不会把换行符作为字符串的内容。
【实例3】不能忽略空白符的情形=
输入示例=
输入a=99=按下回车键=程序竟然运行结束了=只有第一个 scanf() 成功读取了数据=第二个 scanf() 仿佛没有执行一样=根本没有给用户任何机会去输入数据。这是为什么呢=
第一个 scanf() 执行完后=将 99 赋值给了 a=缓冲区中只剩下一个换行符\n=到了第二个 scanf()=发现缓冲区中有内容=但是又不符合控制字符串的格式=于是尝试忽略这个空白符。注意=这个时候的空白符是不能忽略的=所以就没有办法了=只能读取失败了。
实测发现=空白符在大部分情况下都可以忽略=前面的两个例子就是这样。但是当控制字符串不是以格式控制符 %d、%c、%f 等开头时=空白符就不能忽略了=它会参与匹配过程=如果匹配失败=就意味着 scanf() 读取失败了。
本例中=第二个 scanf() 的开头并不是格式控制符=而是写死的b字符=所以不会忽略换行符=而换行符和b又不匹配=怎么办呢=没办法=只能读取失败了。
如果我们换一种输入方式呢=
你看=第二个 scanf() 也读取失败了。执行到第二个 scanf() 时=缓冲区中剩下 b=200↙=开头的空格依然不能忽略=然而又和控制字符串不匹配=所以只能读取失败了。
两种输入方式都不行=究竟该如何输入呢=很简单=不要让两份数据之间有空白符=只能像下面一样输入=
这样 a 和 b 都能够正确读取了。注意=a=99b=200中间是没有任何空格的。
较后=我们再修改一下上面的代码=将第二个 scanf() 改成下面的样子=
运行结果=
此时=第二个 scanf() 的控制字符串以%d开头=就可以忽略换行符了。忽略换行符以后=缓冲区中就没有内容了=所以会等待用户输入。输入 200 以后=第二个 scanf() 就匹配成功了=将 200 赋值给变量 b。
那么=为什么只有当控制字符串以格式控制符开头时=才会忽略换行符呢=我也觉得这个特性很奇怪=目前还未想明白=也没有资料可查=请读者先记住这个结论。
转载:感谢您阅览,转载请注明文章出处“来源从小爱孤峰知识网:一个分享知识和生活随笔记录的知识小站”。
链接:结合C语言缓冲区谈scanf函数,那些奇怪的行为其实都有章可循http://www.gufeng7.com/niaolang/454.html
联系:如果侵犯了你的权益请来信告知我们删除。邮箱:119882116@qq.com