一、getchar 函数
从书本上获得关于 getchar 函数的知识是如此说明的:getchar() 函数将从标准设备中读入一个字符,这里所谓的标准设备指的是键盘。当然也有其他一些关于介绍 getchar() 函数的不同于前的说法,然而不过是笔墨不同,意思是一样的。
getchar() 函数是从缓冲字节流(Stream)中读入字符,C语言以文件流(字节流)来维护设备的数据,在调用 getchar() 函数时,你可以输入一个以上的字符,它们将缓冲到标准输入流(stdin)中,然而 getchar() 函数每次被调用时只会从输入流中读取(函数返回)一个字符,剩余的字符将在下一次被调用时读取,直到读至文件流的尾部(文件流尾部的标志是 EOF),事实上,程序执行中会先判断缓冲区是否为空(检查 FILE 结构的 level 属性),若不为空则取出一个字符,否则调用 _fgetc() 函数向设备读取字符。
getchar() 函数事实上不是一个函数,它是一个宏,被定义在 stdio.h 头文件中,如下:
#define getchar() getc(stdin)
getc(stdin) 在预处理时将替换 getchar(),这在编译前便已经被处理了。
再深入一些,getc() 也不是函数,它与 getchar() 一样,是一个宏,getc() 的宏定义如下:
#define getc(f) \
((--((f)->level) >= 0) ? (unsigned char)(++(f)->curp)[-1] : \
_fgetc (f))
_fgetc() 函数在 stdio.h 头文件如下被声明:
int _Cdecl _fgetc (FILE *stream);
可以猜测得到 _fgetc() 函数遇到回车即刻返回。
stdin 是标准输入流,除了标准输入流外,还有标准输出流(stdout),标准错误输出流(stderr),标准打印流(stdptr),标准辅助设备流(stdaux)。
这五个标准流在 stdio.h 头文件如下被定义(它们也是宏):
#define stdin (&_streams[0])
#define stdout (&_streams[1])
#define stderr (&_streams[2])
#define stdaux (&_streams[3])
#define stdprn (&_streams[4])
_streams 是由外部(系统)定义的 FILE(文件)结构类型的一个数组,它在 stdio.h 中定义如下:
extern FILE _Cdecl _streams[];
getchar() 获得字符是通过读取标准输入文件流,标准输入文件流将缓冲所输入的字符(回车结束输入),下一次调用 getchar() 函数时将从流中读取,除非缓冲区已为空,若缓冲区已为空则调用 _fgetc() 函数读入,_fgetc() 函数将客户所输入的字符缓冲到标准输入文件流,_fgetc() 函数直到遇到回车才结束。
二、while 循环的条件:(c=getchar())!=EOF
首先需要知道一点就是,EOF 是一个宏,在 stdio.h 头文件中被定义为 -1,宏定义如下:
#define EOF (-1)
这个程序与《C陷阱与缺陷》一书中第五章 5.1 节 中的程序几乎是一样的,我将在下面引用该书的相关内容,在引用之前,我补充说明一下,我们是无法直接从键盘上输入 -1 这个值,因为键盘上并没有“-1”这个键,若无法输入,则该循环将会是个死循环,按 Ctrl+Z(屏幕上将显示^Z) 可以向缓冲区插入 EOF 标志。若输入 abcd^Zefgh,即在输入 abcd后按Ctrl+Z,然后再输入 efgh,那么缓冲区中虽然存放了以上字符,然而文件读到d 之后,将认为文件已经读到尾部,所以若要输出的话,将只输出 abcd。
以下摘录该书原中文版的说明,你便可以知道这个条件的可能存在的问题(或许这个问题并不是你所关心的):
5.1 返回整数的 getchar 函数
我们首先考虑下面的例子:
#include <stdio.h>
main()
{
char c;
while((c=getchar()) != EOF)
putchar(c);
}
getchar 函数在一般情况下返回的是标准标准输入文件(流)中的下一个字符,当没有输入时返回 EOF(一个在头文件 stdio.h 中被定义的值,不同于任何一个字符)。这个程序乍一看似乎是把标准输入复制到标准输出,实则不然。
原因在于程序中的变量 c 被声明为 char 类型,而不是 int 类型。这意味着 c 无法容下所有可能的字符,特别是,可能无法容下 EOF。
因此,最终结果存在两种可能。一种可能是,某些合法的输入字符在被“截断”后使得 c 的取值与 EOF 相同;另一种可能是,c 根本不可能取到 EOF 这个值。对于前一种情况,程序将在文件复制的中途终止;对于后一种情况,程序将陷入一个死循环。
实际上,还有可能存在第三种情况:程序表面上似在能够正常工作,但完全是因为巧合。尽管函数 getchar() 的返回结果在赋给 char 类的变量 c 时会发生 “截断”操作,尽管 while 语句中比较运算的操作数不是函数 getchar 的返回值,而是被“截断”的值 c,然而令人惊讶的是许多编译器对上述表达式的实现并不正确。这些编译器确实对函数 getchar 的返回值作了“截断”处理,并把低端字节部分赋给了变量 c。但是,它们在比较表达式中并不是比较 c 与 EOF,而是比较 getchar 函数的返回值与 EOF!编译器如果采取的是这种做法,上面的例子程序看上去就能够“正常”运行了。
[此贴子已经被作者于2006-10-6 4:19:06编辑过]