映月读书网 > C语言解惑 > 20.1 getchar函数的返回类型不是字符 >

20.1 getchar函数的返回类型不是字符

【例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
?0
How are you
?
  

如果只输入1行信息,可以定义回车结束输入,即


#undef EOF
#define EOF \'n\'
  

【例20.4】为什么下面程序也能正常运行?


#define EOF -1
int 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。