3.4进阶:用getchar()输入数据存在的问题(含错误案例分析)
3.4进阶:用getchar()输入数据存在的问题(含错误案例分析)
用getchar函数输入字符型数据时,存在的主要问题:
请看下列程序
这个程序就是先执行getchar函数调用,从键盘输入一个字符,然后在输出一个字符,接下来,在执行getchar函数调用,从键盘输入一个字符,并且输出一个字符。那么这个程序能否按照我们的预期,来输出这样的结果呢?下面来运行一下
当程序执行到第五行语句的时候,就等于用户在键盘上输入一个字符,假设我们输入的字符是a,但是用户输入完a以后,还要按下一个回车键子。才能够真正把字符读走。
当我们按下回车键子,程序输出了ch1这个字符型变量的值,确实是字符a,但是程序执行到7行时,应该让用户在输入一个字符,而程序并没有,让用户输入一个字符就直接执行了8行语句,输出了ch2等于,而且ch2这个值似乎是一个回车换行符号,那么究竟ch2读入的是不是回车换行符呢?下面,我们采用,单步运行的方式来验证我们分析的结果。
首先 将光标移动到第六行的位置,执行run to cansen就是运动到光标所在位置处,同样我们输入a按下回车键子,这个时候,打开观察窗,发现,ch1,读入的字符,确实是a,在接下来,采用单步运行方式next line执行到下一行的语句。此时第六航语句输出完了,此时确实是输出的ch1的a,然后将要执行的语句,是第七行语句,也就是黄色的箭头指示的,仍然单步运行,此时发现ch2读入的是一个回车符号,因为\n表示一个回车符号,它的ascii码值,就是10,通过观察窗观察变量的变化,我们验证了刚才的分析结果,就是地七行语句执行getchar执行函数调用的时候,读入的是用户从键盘输入的回车符号,也就是在执行第一条语句执行getchar函数调用的时候,我们输入了一个字符a,然后按了一个回车符,用户从键盘输入的字符a,被第五行getchar函数给读走了。而用户在输入a之后,输入的回车符号,被第七行getchar给读走了,当执行到第八行语句的时候,我们看到屏幕输出的ch2等于后边的变量值,确实是一个回车换行符,因为光标停留在在下一行的起始位置。下面来考虑这样的问题,当程序执行到第五行语句的时候,假设用户从键盘输入的时候,不是一个,而是多个字符的话,那么此时程序的输出结果那么会是什么呢?
运行一下,
假设我们输入的是ab,然后按下回车键子,那么果然第五行getchar函数读入的是用户读入的字符a,而第七行的getchar函数读入的是用户的字符b,同样的我们可以用单步运行的方式来,验证我们分析的结果。
首先将光标放在第六行语句,运行到光标所在位置处,然后输入ab,回车,这个时候,由于第五行语句被执行完了,因此,ch1得到是用户输入的字符a,接下来执行第六行语句,屏幕输出的是变量值a,在接下来,执行下一行语句,执行完第七行语句我们发现,ch2得到的是字符变量b,在接下来执行完第八行语句的时候,我们观察,程序执行的结果,就是输出了ch2变量的值,
为什么会有这样的结果。
原因就是用getchar输入字符的时候,它会将所有的用户输入,包括回车符号,都放在输入缓冲区中。
标准C定义的getchar函数,是基于unix兼容的,也就是说用户用getchar函数,从键盘输入字符的时候,程序并未直接读取用户的输入,而是将用户输入的字符,先放入到一个输入缓冲区队列中,直到用户键入了一个回车符,或者遇到了一个文件结束符号的时候,程序才认为用户的输入结束了,当一行输入结束以后,getchar函数才开始从输入缓冲队列中,读取字符,而前面函数没有读走的数据,仍然是留在里缓冲区队列中的,将会被下一个getchar函数,来读取,这就是所谓的行缓冲输入方式也许你会问,难道getchar函数不是以字符为单位进行读取数据的么?既然我输入了第一个字符a,那么执行到printf函数调用语句的时候,就应该输出字符a,而我输入了字符b,那么执行到printf的时候,就应该输入字符b啊,为什么不是这样的呢?初学者在一开始学习getchar的时候,通常都是这样想的,但是程序,却不是这样执行的,而b是要在读入了一个回车换行的时候,才进行一次输出,如果用户在按回车键,之前输入了不止一个字符,那么其他的字符,如果没有被读走的话,仍然是留在输入缓冲区队列中的,等待这后面的getchar函数来读取,也就是说,如果缓冲区队列中,还有字符型数据的话,那么后面的getchar函数就不会等待用户按键,就直接从输入缓冲区队列中,读取字符,直到缓冲区中的所有队列都读走以后,才会等待用户去按键,输入新的字符,因此,在使用getchar函数的时候,要求用户输入字符以后,必须按下一个回车键,而且这个回车键的作用相当于一次输入把用户输入的字符,都放入到输入缓冲区的队列中,等待用户从缓冲区队列中去,读取字符,
getchar函数要读到一个回车符号,或者文件结束符,才进行一次处理操作的更深层次的原因是什么呢?
也就是说为什么getchar函数是以行为单位,而不是以字符为单位读取字符的呢?其实大师在当初设计c语言的时候,所有的输入都时候按照文件的方式来读取字符的,而文件都是以行为单位的,因此,只有遇到换行符,那么程序才会认为输入结束,然后才会去执行程序的其他语句,也就是getchar函数实际上是一个文件操作,而文件是以行为单位的,因此getchar函数也是以行位单位,读取数据的,这就是为什么getchar函数要读到一个换行符,或者是一个文件结束符,才进行一次处理操作的真正原因。
在使用getchar函数的时候,另外一个注意的事情,就是getchar函数返回的是用户从键盘键入的第一个ASCII值,而这个ASCII值,通常情况下都是非负的,但是也有例外,在有些情况下,它也会返回赋值,就是当用户在unix或者是linux下,键入组合键ctrl+D的时候,在WINDOS下,ctrl+z的时候,那么也会返回,一个赋值,就是返回eof。eof是一个符号常量,它表示一个文件结束符号,通常被定义为负一。在这种情况下,getchar函数返回一个负值,而把一个负值赋值给一个字符型变量是不正确的,也是难以理解,为了使变量能够包含getchar函数返回的所有的可能的值,正确的定义方式,应该是这样的
定义一个整型变量,然后把getchar函数返回值赋值给这个整型变量。