详解scanf、gets、getchar和getch 使用及其原理。

scanf、gets、getchar和getch 使用及其原理。

一、说在最前:回车及换行。

概念

在计算机还没有出现之前,有一种叫做电传打字机。在电传打字机打字时,在每行后面加两个表示结束的字符,分别叫做回车和换行。

回车:是告诉打字机把打印头定位在左边界,不卷动滚筒;符号 \r;十六进制 0x0d

换行:是告诉打字机把纸张向下方移动一行,不改变左右位置;符号 \n;十六进制 0x0a

区别

Unix系统里,每行结尾只有"<换行>",即按下Enter后会产生 ‘\n’ ( linux与 unix系统基本一致)

Win系统里,每行结尾是"<回车><换行>",即按下Enter后会产生 ‘\r’ 和 ‘\n’

Mac系统里,每行结尾是"<回车>",即按下Enter后会产生 ‘\r’
上述系统按下 Enter 后都会进行回车和换行操作,而产生的 ‘\r’ ‘\n’ 则会保留在 输入缓冲区

二、缓冲区

一般情况下,由键盘输入的字符并没有直接送入程序,而是被存储在一个缓冲区当中+。缓冲又分为两种,行缓冲和完全缓冲。对于完全缓冲来说,缓冲区满时被清空(内容被发送到指定的目的地)。这种缓冲通常出现在文件输入中。对于行缓冲来说,遇到一个换行符,则清空缓冲区,键盘输入是标准的行缓冲,因此,按下换行键(回车键)的时候才会清空(键盘缓冲区)。
键盘缓冲区的数据接下来会被传送到 输入缓冲区(stdin), 输入函数scanf、gets、getchar都是从输入缓冲区中获取数据的。如果输入数据过多或者输入缓冲区还有很多数据,那么下次调用输入函数时,不会要求你输入,而是直接从输入缓冲区中读取数据。
在这里插入图片描述

三、scanf、gets、getchar和getch 区别及一些原理。

1. scanf

scanf 指定了输入的格式,并按照格式说明符解析输入对应位置的信息并存储于可变参数列表中对应的指针所指位置。

从 scanf() 角度看输入

假设scanf() 根据一个%d转换说明读取一个整数:scanf 函数每次从输入缓冲区中读取一个字符,跳过所有空白字符,直到遇到第一个非空白字符才开始读取。因为要读取整数,所以 scanf 希望发现一个数字字符或者是 ’ + ’ 或 ’ - ’ 。如果找到一个数字或者 ’ + ’ 或 ’ - ’ ,它便保存该字符,并读取下一个字符。scanf 不断地读取和保存,直到遇到非数字字符。如果遇到一个非数字字符,它便认为读到了整数的末尾。然后 scanf 把非数字字符放回到输入缓冲区中。( 这就意味着程序在下次读取时,首先读到的是上一次读取时丢弃的非数字字符 ) 最后 scanf 计算已读数字相应的数值,并将计算后的值放入指定的变量中。

如果使用%s转换说明:scanf 会读取除空白字符以外的所有字符。scanf 跳过空白开始读取第1个非空白字符,并保存直到再次遇到空白字符。这就意味着 scanf 根据%s 转换说明读取一个不包括空白字符的字符串。当scanf 把字符串放进指定数组中时,它会在字符序列末尾加上’\0’,让数组内容成为一个C字符串。
空白符(空白符:指空格符、制表符、回车符)

例子解析
	现在假设你的缓冲区里有:abcd\n1234\n  (其中\n是回车符)  执行:scanf("%s",name);的时候
由于scanf是读数据直到看见空白符(空白符:指空格符、制表符、回车符)就停止的输入函数所以执行后,把abcd存到了name中。
缓冲区于是变成了 :\n1234

	接下来的执行就有问题了,如果遇到了: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例外,不会放弃先导空白符,而是直接读取第一个字符 )。切记:键盘输入最后按下的回车键 会以 ‘\n’ 的形式存放进输入缓冲区,当scanf 以%c读取时会读入上一次键盘输入末尾的 \n 导致读取数据错误,所以用%c时要清空输入缓冲区,以免读取到垃圾数据。

注意:scanf("%d\n",&num); \n在scanf格式串中不表示等待换行符,而是读取并放弃 (丢掉,不放回输入缓冲区) 连续的空白字符。(事实上,scanf格式串中的任何空白字符都表示读取并放弃空白字符。而且,诸如%d这样的格式也会扔掉前边的空白,因此你通常根本不需要在scanf格式串中加入显式的空白。)
因此,"%d\n"中的\n会让scanf读到非空白字符为止,而它可能需要读到下一行才能找到这个非空白字符。这种情况下,去掉\n仅仅使用"%d"即可。

2. gets

gets从标准输入设备读字符串函数,可以无限读取,不会判断上限,以回车结束读取。所以如果输入的字符串超过100个,它也不会做检测,此时就会发生溢出。若输入未超过100个,则gets从输入缓冲区中读取数据 当读取到 \n
时,将 ‘\n’ 转换成 ‘\0’ 作为字符串结束标志。(\n并没有被放回去,所以不会产生像 scanf 读取完后留下 \n, 而导致下次读取单个字符的错误。)

3. getchar

从输入缓冲区中读取一个字符,若缓冲区中无数据就会等待用户输入,直到输入回车键,将数据输入到输入缓冲区。getchar可以用来清空缓冲区(防止读入垃圾数据)。
可以写成如下函数:

void clear_in(void)
{
	int c;
	while( (c=getchar() ) != '\n' && c != EOF);
}

getchar会一直读取字符直到输入缓冲区内没有数据(返回一个EOF = End Of File 文件结束),这样输入缓冲区就清空干净了。

4. getch

getch直接从键盘获取键值,不等待用户按回车,只要用户按一个键,getch就立刻返回。
getch是非缓冲输入函数,即不能用getch来接收缓冲区已存在的字符。
getch是无回显的读取 (不显示读取键值)。

注意:getch();并非标准C中的函数,不存在C语言中。所以在使用的时候要注意程序的可移植性。
1.所在头文件是conio.h。而不是stdio.h。
2.在使用之前要调用 initscr(),结束时要调用 endwin()。否则会出现不输入字符这个函数
也会返回的情况。
3.若使头文件 getch.h ,也会出现不输入字符这个函数直接返回的情况,可以使用多个getchar()来清空缓冲区。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值