想象输入设备(键盘)连接着一个叫“缓冲”的东西,把缓冲认为是一个字符数组。当你的程序执行到scanf时,会从你的缓冲区读东西,如果缓冲区是空的,就阻塞住,等待你从键盘输入。 现在假设你的缓冲区里有: abcd/n1234/n (其中/n是回车符) 执行: scanf("%s",name); 的时候,由于scanf是读数据直到看见空白符(空白符:指空格符、制表符、回车符)就停止的输入函数。所以执行后,把abcd存到了name中。缓冲区于是变成了 /n1234/n 接下来的执行就有问题了,如果遇到了: scanf("%d",&number); 怎么办?因为遇到了回车符,它并不是一个数字,所以应该抛出错误,让程序死掉?不对吧,明明是编程序的你让用户输入完名字回车的(不然怎么提示用户该输入数字了?),怎么能出尔反尔地报错呢? 所以scanf还有一个特性,就是忽略先导的空白符。不管是有几百个回车也好,几万个空格也罢,只要它们连续地出现在缓冲区的开头,就统统忽略他们。然后再读有意义的字符。于是1234被读入number。 回到刚刚,当缓冲区还是: /n1234/n 的时候,如果遇到了: scanf("%c",&sex); 应该怎么办呢?你说,那好办呀,不是说了忽略前导空白符吗?跳过回车读'1'呀! 想法是好的,可这只针对你的程序这一种情况。如果我编写的程序就是统计用户输入了多少个回车呢?缓冲里明明有回车符,却读不出来,你不是忽悠我吗? 所以对scanf来讲跳过前导空白符有个例外,当参数是%c的时候,就把缓冲区的第一个字符返回回去,不管是什么。 这样的设计就有个问题,scanf对不同的参数表现出来的特性不一样。 得承认,这是个缺陷,但不是说这样不好。 这样的设计至少把发现所有字符的机会交给了用户, 设计者这样想: 如果程序员使用了scanf("%c",..),那他就有必要知道这函数能把回车符读出来,至于程序员对回车符感不感兴趣,那就看他了,不感兴趣的话,程序员也一定知道该怎么处理。 回到你的程序里。 当执行scanf("%s",name)的时候,要求你从键盘输入, 于是你输入了"abc",然后“回车”。 缓冲区里自然而然地是: abc/n scanf把abc拿走了,留下了/n,缓冲区里现在就剩下/n 于是,下一个scanf ("%c",&sex); 想当然地读取了/n 明白了? 关于scanf忽略前导空白符这一点,可以这样验证: 写个程序,用scanf()读数据,只要不是%c就行。 然后输入的时候,随便输入回车、空格、制表符,然后“回车”确认。 会发现程序依然提示等待你输入。 就是因为它忽略掉所有前导空白符之后发现缓冲区是空的!于是乖乖地阻塞住,等待你输入。 关于scanf是直到看见空白符结束读取这一点,如果你是初学C的话,那么很快你就会遇到另一个函数,叫gets()。 程序里如果我们想一次读入一个英文句子: I am a student. 如果你用scanf读的话,只能读出"I",想读出后面的东西要不断调scanf。 此时需要用gets,这个函数不管是什么一律读进来,直到遇到回车符才停下。 总之,各有各的用途,全都熟悉之后,才能在恰当的时候恰当地使用。
关于scanf
最新推荐文章于 2021-11-06 17:31:53 发布