讀古今文學網 > 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。