许多初学者都习惯用 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 0A | 00 00 00 0A |
                  | 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);          // 正确

C/C++误区四:char c = getchar();的更多相关文章

  1. codevs1004四子连棋[BFS 哈希]

    1004 四子连棋   时间限制: 1 s   空间限制: 128000 KB   题目等级 : 黄金 Gold   题目描述 Description 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗 ...

  2. COJ966 WZJ的数据结构(负三十四)

    WZJ的数据结构(负三十四) 难度级别:C: 运行时间限制:20000ms: 运行空间限制:262144KB: 代码长度限制:2000000B 试题描述 给一棵n个节点的树,请对于形如"u  ...

  3. COJ986 WZJ的数据结构(负十四)

    WZJ的数据结构(负十四) 难度级别:D: 运行时间限制:6000ms: 运行空间限制:262144KB: 代码长度限制:2000000B 试题描述 请你设计一个数据结构,完成以下功能: 给定一个大小 ...

  4. COJ 0260 HDNOIP201204四个国王

    HDNOIP201204四个国王 难度级别:A: 运行时间限制:1000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 在N*M的棋盘上摆国际象棋中的“国王”.如果两 ...

  5. NOIP2015普及组第四题推销员

    好久没有写博客了,今天再写一篇.还是先看题: 试题描述 阿明是一名推销员,他奉命到螺丝街推销他们公司的产品.螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户.螺丝街一共有 N 家 ...

  6. bzoj3796(后缀数组)(SA四连)

    bzoj3796Mushroom追妹纸 题目描述 Mushroom最近看上了一个漂亮妹纸.他选择一种非常经典的手段来表达自己的心意——写情书.考虑到自己的表达能力,Mushroom决定不手写情书.他从 ...

  7. 1111: [POI2007]四进制的天平Wag

    1111: [POI2007]四进制的天平Wag 链接 题意: 用一些四进制数,相减得到给定的数,四进制数的数量应该尽量少,满足最少的条件下,求方案数. 分析: 这道题拖了好久啊. 参考Claris的 ...

  8. bzoj 1111 [POI2007]四进制的天平Wag 数位Dp

    1111: [POI2007]四进制的天平Wag Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 302  Solved: 201[Submit][St ...

  9. CCF-NOIP-2018 提高组(复赛) 模拟试题(四)

    T1 贪吃蛇 [问题描述] 贪吃蛇是一个好玩的游戏.在本题中,你需要对这个游戏进行模拟. 这个游戏在一个 \(n\) 行 \(m\) 列的二维棋盘上进行. 我们用 \((x, y)\) 来表示第 \( ...

随机推荐

  1. STL容器是否是线程安全的

    转载http://blog.csdn.net/zdl1016/article/details/5941330 STL的线程安全. 说一些关于stl容器的线程安全相关的话题. 一般说来,stl对于多线程 ...

  2. javascript 继承机制设计思想

    作者: 阮一峰 原文链接:http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_java ...

  3. (转)Eclipse 远程调试 WebSphere Application Server (WAS)

    目前我们项目中使用的应用服务器多是WebSphere,一直苦于无法进行调试,今天在网上看到一篇,原文是 http://www.cnblogs.com/newstar/archive/2010/04/1 ...

  4. 使用cx_Freeze 将python3代码打包成.exe程序

    在这里分享一下如何在py3下使用cx_Freeze打包pyqt5的程序 首先吐槽下,深深鄙视一下百度,各种百度各种没有,之前我在py2.7下使用pyqt4开发过一个小软件,用的是py2exe进行打包的 ...

  5. [Protractor] Protractor Interactive with elementor

    Install: npm install -g elementor Then run: webdriver-manager start Lets say if we want to test 'htt ...

  6. web去掉浏览器自带默认样式

    @charset "utf-8"; ;;} body{font-size:12px;} img{border:none;} ul,ol{list-style:none;} inpu ...

  7. FullCalendar 的学习笔记(一)

    前一段时间,一个老项目需要新增一个小功能,日程表~ 于是网上找了下,发现FullCalendar这个控件还不错于是就拿来用了下,下面简单介绍下我的学习笔记. 首先就是了解下FullCalendar的A ...

  8. ado.net数据库操作(2)

    5.1使用SQLDataReader进行数据库查询 <%@ Import Namespace="System.Data" %> <%@ Import NameSp ...

  9. Android-------设置TextView同时显示图片和文本,并控制图片大小

    //获取资源图片     Drawable leftDrawable = getResources().getDrawable(R.drawable.comment_parise);     //设置 ...

  10. NSBundle 类

    NSBundle NSBundle继承于NSObject,NSBundle是一个程序包,其中包含了程序会使用的资源(图像,声音,编辑好的代码,nib文件). 一. 初始化NSBundle + (ins ...