【例20.1】下面程序的是把输入复制到输出,分析程序中的错误。
#include <stdio.h>int main( void ){ while (getchar()!=EOF) putchar(getchar()); printf(/"n/"); return 0;}
【解答】当程序调用getchar时,程序就等着用户输入,如果用户输入不止一个字符,这些字符都会被存放在键盘缓冲区中。直到用户按回车(回车字符也放在缓冲区中)时,getchar才开始从stdin流中每次读入一个字符。等到缓冲区中的字符读完后,再等待用户输入。
在while语句里面的getchar函数用来判断,第2个getchar函数做为putchar函数的参数,将字符输出到显示设备。由此可见,第1个getchar读的是缓冲区中的奇数字符,第2个getchar读的则是偶数字符。所以这个程序只将输入偶数位的字符复制到显示设备上。下面是一个运行示例。
Welcome!ecm!
【例20.2】下面的程序能把输入复制到输出,这个程序正确吗?
#include <stdio.h>int main( void ){ char c; while ((c=getchar())!=EOF) putchar(c); printf(/"n/"); return 0;}
程序使用一个字符变量存储getchar函数的返回值。表面看来似乎正确,其实并非如此。因为常常用getchar函数读取字符,所以被认为是字符型函数。它的原型要追述到getc函数,因为getchar是使用getc定义的宏,即
#define getchar() getc(stdin)
而getc又是定义的宏,这里就不追究下去了。getc返回类型是整数类型,故有
int getchar(void);
getchar函数的返回值是用户输入的第一个字符的ASCII码值,如出错则返回-1。
读字符时遇到文件结束符,函数返回一个文件结束符标志EOF,EOF在stdio.h中定义为-1。
注意:EOF是定义在头文件中的一个值。这个值不同于任何一个字符。EOF不是可输出字符,因此不能在屏幕上显示。getchar是返回整数的函数,在一般情况下确实返回的是标准输入文件中的下一个字符,但当没有输入时,返回的却是EOF。
现在把变量声明为字符型而不是整型,这就暗示可能不能接受EOF,即意味着无法接收所有可能存在的字符。这就可能存在如下三种情况。
(1)输入的某些合法字符被“截断”处理,即把低位字节赋给变量c,使c的取值与EOF相同,从而使程序在文件复制的中途停止。
(2)c不可能取得EOF这个值,程序进入死循环。
(3)由于巧合,使程序好像能够“正常”工作。这是因为有许多编译系统虽然对函数getchar的返回值进行了“截断”处理,但它们在比较表达式中并不是比较EOF和c,而是比较函数getchar的返回值与EOF。尽管这种实现并不正确,但却使程序能够“正常”运行。
由此可知,正确的程序应编写如下。
#include <stdio.h>int main( void ){ int c; while ((c=getchar())!=EOF) putchar(c); printf(/"n/"); return 0;}
EOF是为getchar函数读入文件而设计的结束符,不是从键盘输入的单字符。所以如果要结束运行,必须执行Ctrl+C。
上面程序追求简洁,下面程序是条理清楚。
#include <stdio.h>int main( void ){ while(1) { int c; c=getchar(); if(c==EOF) break; putchar(c); } return 0;}
【例20.3】为什么下面程序在有的系统中第1次编译时会给出警告信息?
#include <stdio.h>#define EOF /'0/'int main( void ){ int c; while ((c=getchar())!=EOF) putchar(c); printf(/"n/"); return 0;}
【解答】因为在该系统的stdio.h中将EOF定义为-1,这里变成重复定义。应先取消原来的定义,然后再将EOF定义为字符0,即
#undef EOF#define EOF /'0/'
运行示范如下。
We are here!We are here!How are you?0How are you?
如果只输入1行信息,可以定义回车结束输入,即
#undef EOF#define EOF /'n/'
【例20.4】为什么下面程序也能正常运行?
#define EOF -1int main( void ){ register int c; while ((c=getchar())!=EOF) putchar(c); printf(/"n/"); return 0;}
【解答】getchar宏定义在stdio.h中。在没有包含头文件stdio.h时,编译器会假定getchar是一个返回类型为整型的函数。忽略警告信息继续编译即可生成可执行文件。
为了预防编程者粗心大意忘记包含头文件stdio.h,很多C语言实现在库文件中都包含有getchar函数(这也为了方便那些需要得到getchar地址的编程者)。
不过,由于忘记包含头文件stdio.h,就会在所有出现getchar宏的地方,都用getchar函数调用来替换getchar宏。因为函数调用所导致的开销增多,所以会使程序运行变慢。因为putchar的实现方法与getchar一样,所以这个分析也同样适合putchar。