C/C++ 誤區五:char c = getchar();

來源:互聯網
上載者:User

 

    許多初學者都習慣用 char 型變數接收 getchar、getc,fgetc 等函數的傳回值,其實這麼做是不對的,並且隱含著足以致命的錯誤。getchar 等函數的傳回值類型都是 int 型,當這些函數讀取出錯或者讀完檔案後,會返回 EOF。EOF 是一個宏,標準規定它的值必須是一個 int 型的負數常量。通常編譯器都會把 EOF 定義為 -1。問題就出在這裡,使用 char 型變數接收 getchar 等函數的傳回值會導致對 EOF 的辨認出錯,或者錯把好的資料誤認為是 EOF,或者把 EOF 誤認為是好的資料。例如:

        int c;  /* 正確。應該使用 int 型變數接收 fgetc 的傳回值 */
        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 1A  | 00 00 00 1A |
                  |  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);          // 正確

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.