许多初学者都习惯用 char型变量接收getchar、getc,fgetc等函数的返回值,其实这么做是不对的,并且隐含着足以致命的错误。getchar等函数的返回值类型都是int型
下面是getchar()的定义:
[html]view plaincopyprint?
1int
2getchar ()
3{
4 int result;
5 _IO_acquire_lock (_IO_stdin);
6 result = _IO_getc_unlocked (_IO_stdin);
7 _IO_release_lock (_IO_stdin);
8 return result;
9}
当这些函数读取出错或者读完文件后,会返回 EOF.EOF是一个宏,标准规定它的值必须是一个int型的负数常量。
通常编译器都会把 EOF定义为-1.问题就出在这里,使用char型变量接收getchar等函数的返回值会导致对EOF的辨认出错,或者错把好的数据误认为是EOF,或者把EOF误认为是好的数据。
Int c;
while ( (c = fgetc(fp)) != EOF )
{
Putchar=C;
}
如上例所示,我们很多时候都需要先用一个变量接收 fgetc等函数的返回值,然后再用这个变量和EOF比较,判断是否已经读完文件。
上面这个例子是正确的,把 c定义为int型保证了它能正确接收fgetc返回的EOF,从而保证了这个比较的正确性。但是,如果把c定义为char型,则会导致意想不到的后果。
首先,因为 fgetc等函数的返回值是int型的,当赋值给char型变量时,会发生降级,从而导致数据截断。
例如:
---------------------------------
|十进制|int|char |
|--------|--------------|-------|
|10| 00 00 00 0A|0A|
|-1| FF FF FF FF|FF|
|-2| FF FF FF FE|FE|
---------------------------------
在此,我们假设 int和char分别是32位和8位的。
由上表可得,从 int型到char型,损失了3个字节的数据。当我们要拿char型和int型比较的时候,char型会自动升级为int型。
char 型升级为int型后的值会因为它到底是signed char还是unsigned char而有所不同。
不幸的是,如果我们没有使用 signed或者unsigned来修饰char,那么我们无从知晓char到底是指unsigned char还是指signed char,因为这是由编译器决定的。
不过,无论 char是signed的也好,unsigned的也罢,都不能改变使用char型变量接收fgetc等函数的返回值是错误的这个事实。
唯一能改变的是该错误导致的后果。前面我们说了,char型和int型比较时,char会自动升级为int.
下面我们来看看 signed char和unsigned char在转换成int后,它们的值有什么不同:
---------------------------------------
|char |unsigned|signed|
|-------|---------------|-------------|
|10|00 00 00 0A| 00 00 00 0A |
|FF|00 00 00 FF| FF FF FF FF |
|FE|00 00 00 FE| FF FF FF FE |
---------------------------------------
由上表可知,当 char是unsigned的时候,其转换为int后的值是正数。也就是说,假如我们把c定义为char型变量,而编译器默认char为unsigned char,那么以下表达式将永远成立。
(c = fgetc(fp))!= EOF/* c的值永远为正数,而标准规定EOF为负数*/
也就是说以下循环是一个死循环。
while ( (c = fgetc(fp)) != EOF ){putchar(c);}
读到这里,可能有些读者朋友会说:“那么我明确把 c定义为signed char型的就没问题了吧!”很遗憾,就算把c定义为signed char,仍然是错误的。假设fgetc等函数读到一个字节的值为FF,那么返回值就是00 00 00 FF.把这个值赋值给c后,c的值变成FF.然后c的值为了和EOF比较,会自动升级为int型的值,也就是FF FF FF FF.从而导致以下表达式不成立。(c = fgetc(fp)) != EOF/*读到值为FF的字符,误认为EOF */
也就是说以下循环在没有读完文件的情况下提前退出。
while ( (c = fgetc(fp)) != EOF ){putchar(c);}
综上所述,使用 char型变量接收fgetc等函数的返回值是错误的,我们必须使用int型变量接收这些函数的返回值,然后判断接收到的值是否EOF.只有判断发现该返回值并非EOF,我们才可以把该值赋值给char型变量。
同理,C++中,用char型变量接收cin.get() 的返回值也是错误的。不过,把char型变量当作参数传递给cin.get则是正确的。
例如:
char c = cin.get();//错误,理由同上char c;cin.get(c);//正确
第一个getchar()时输入一个字符,按下回车,那么等于你输入了两个字符,一个是你输入的字符被c1读走,还有一个是回车的转义字符。它还在缓冲区中,那么你的第二个getchar()就读走这个回车的转义字符。所以会自己跳过去。你可以在第二个getchar()之前在放一个getchar(),让这个getchar()把缓冲区里的回车字符读掉。然后你就可以输入了。当然你也可以调用相关函数把输入缓冲区flush掉。
getchar有一个int型的返回值.当程序调用getchar时.
程序就等着用户按键.用户输入的字符被存放在键盘缓冲区中.直到用户按回车为止(回车字符也放在缓冲区中).
当用户键入回车之后,getchar才开始从stdio流中每次读入一个字符.
getchar函数的返回值是用户输入的第一个字符的ASCII码,如出错返回-1,且将用户输入的字符回显到屏幕.
如用户在按回车之前输入了不止一个字符,其他字符会保留在键盘缓存区中,等待后续getchar调用读取.
也就是说,后续的getchar调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完为后,才等待用户按键.
getch与getchar基本功能相同,差别是getch直接从键盘获取键值,不等待用户按回车,只要用户按一个键,getch就立刻返回, getch返回值是用户输入的ASCII码,出错返回-1.输入的字符不会回显在屏幕上.getch函数常用于程序调试中,在调试时,在关键位置显示有关的结果以待查看,然后用getch函数暂停程序运行,当按任意键后程序继续运行.
可以利用getchar()函数让程序调试运行结束后等待编程者按下键盘才返回编辑界面,用法:在主函数结尾,return 0;之前加上getchar();即可