最近在重温K&R的C语言圣经,第二章中的练习题2-2引起了我的注意。

原题是:

Write a loop equivalent to the for loop above without using && or ||.

题目里说的for循环是下面这个:

for (i=; i < lim- && (c=getchar()) != '\n' && c != EOF; ++i)
s[i] = c;

不能用&&和||运算符,又要与for循环中的3个条件表达式等价,怎么改呢?

因为书中这一节正在讲关系运算符和逻辑运算符,其中提到了当表达式结果为true时的值实际上是整数1,为false时为0,即:

true ==
false ==

所以我想到了一种方法:

for (i=; (i<lim-) + ((c=getchar())!='\n') + (c!=EOF) == ; ++i)
s[i] = c;

3个表达式都成立,也就是说它们的值都是1,所以和为3。

看起来好像没问题,但是我发现这么修改却是不对的。

因为在原程序里的3个表达式是有先后次序并且有“短路”关系的——当第一个表达式“i<lim-1”为false时,后面两个就不会再执行了;而且也不应该执行,否则就会导致字符丢失。

为什么呢?假如现在缓冲区里有字符“abcdefg”,lim为3:

当i为2时,“i<lim-1”为false,如果程序是非短路的,此时会继续执行后面的c=getchar(),就会把第3个字符即"c"读取出来,但由于整个循环的条件为false,因此不会执行赋值语句“s[i]=c”,所以这个字符就这么丢失了。

而我修改后的程序恰恰是非短路的!但我却想不出来一个可以和原程序完全等价的方案。

评论中@剑指天涯不可挡 和 @nicozhang 两位朋友指出可以用条件表达式或将条件判断放到循环体中来解决这个问题。但是书中到目前为止还没有介绍if语句和条件表达式。如果这个题目是想考察if语句或条件表达式,那么它就不应该在这里出现。所以我觉得这是一个作者也没有考虑到的Bug,你不可能写出和原程序完全等价的程序而不用到&&和||。

毕竟两位作者也是人。

当然也可能是我错了,如果你有好的解决方案请不吝赐教。


20151016补充:

今天回过头来看这篇文章,重新思考这个问题,忽然想到一个解决方法:

// for (i=0; i < lim-1 && (c=getchar()) != '\n' && c != EOF; ++i)
// s[i] = c; for (i=; (i < lim-2) + ((s[i]=getchar()) != '\n') + (s[i] != EOF) == ; ++i)
;

把c用s[i]代替就不会出现上面所说的字符丢失的问题了。并且此时必须注意,第一个判断i < lim-1需要修改为i < lim-2。因为修改后的条件表达式不是短路的,如果不这样修改,那么当i等于lim-1时并不会马上退出循环,而是会继续执行后面两个子表达式,然后才退出循环,这样程序就错了。

因此这道题并没有Bug,2位大师是冤枉的,我在此郑重向他们道歉!

K&R《C语言》书中的一个Bug的更多相关文章

  1. 学习K&R时初学者经常遇到的一个问题——EOF

    学习K&R时初学者经常遇到的一个问题——EOF

  2. C++primer原书中的一个错误(派生类using声明对基类权限的影响)

    在C++primer 第4版的 15章 15.2.5中有以下这样一段提示: "注解:派生类能够恢复继承成员的訪问级别,但不能使訪问级别比基类中原来指定的更严格或者更宽松." 在vs ...

  3. 微软BI 之SSIS 系列 - MVP 们也不解的 Scrip Task 脚本任务中的一个 Bug

    开篇介绍 前些天自己在整理 SSIS 2012 资料的时候发现了一个功能设计上的疑似Bug,在 Script Task 中是可以给只读列表中的变量赋值.我记得以前在 2008 的版本中为了弄明白这个配 ...

  4. 记录一个使用HttpClient过程中的一个bug

    最近用HttpClient进行链接请求,开了多线程之后发现经常有线程hang住,查看线程dump java.lang.Thread.State: RUNNABLE at java.net.Socket ...

  5. yarn client中的一个BUG的修复

    org.apache.spark.deploy.yarn.Client.scala中的monitorApplication方法: /** * Report the state of an applic ...

  6. python日志轮转RotatingFileHandler在django中的一个bug

    简介 大量过时的日志会占用硬盘空间,甚至长时间运行不注意会占满硬盘导致宕机,那么就可以使用内建logging模块根据文件大小(logging.handlers.RotatingFileHandler) ...

  7. 关于ligerUI中ligerTree代码中的一个bug,造成该控件无法通过url的POST方式加载数据

    该bug造成ligerTree参数中的method无论你怎么设置都只能用get方式提交 由于本人水平有限,只是找到原因,但无法修正 ligerUI v1.1.9 版本中的ligerui.all.js文 ...

  8. tensorflow代码中的一个bug

    tensorflow-gpu版本号 pip show tensorflow-gpu Name: tensorflow-gpu Version: 1.11.0 Summary: TensorFlow i ...

  9. 【紫书】BigInteger 高精度类型 原书上有一个bug:A+B!=B+A

    存个代码 struct BigInterger { static const int BASE = 1e8; ; vector<int> s; BigInterger() { *this ...

随机推荐

  1. ZJOI2018 D1

    归途的车上满是悲伤的气息 虽然早就预言到D1会滚粗,但一切都结束之后还是特别难过. 延时15min 50min T1 30pts 1.5h T2 10pts 2.5h T1 50pts 4.5h T3 ...

  2. ZendFramework安装配置

    1.创建项目目录 E:/wwwroot/myzfpro 运行cmd后,cd到框架源文件的bin目录 运行zf.bat创建命令:zf.bat create project E:/wwwroot/myzf ...

  3. 解题:HNOI 2008 玩具装箱

    题面 搞了一晚上斜率优化,大概懂了一点,写写 原来常用的优化dp方法:做前缀和,预处理,数据结构维护 现在有转移方程长这样的一类dp:$dp[i]=min(dp[i],k[i]*x[j]+y[j]+c ...

  4. Shell颜色及显示git分支配置

    # Ubuntu终端下命令行颜色配置 ## Parses out the branch name from .git/HEAD: find_git_branch () { local dir=. he ...

  5. oracle-DECODE()函数

    DECODE()函数 DECODE(value, if1, then1, if2,then2, if3,then3, . . . else ) 含义解释: DECODE(条件,值1,翻译值1,值2,翻 ...

  6. (转)C#中“EQUALS”与“==”的速度比较

    结论: true判断时,用"value" == string是最快的:false判断时,用"value".Equals(string)是最快的. 也就是说:一个 ...

  7. scrapy 采集网页出现丢失url的问题

    url_list = ["http://www.icoat.cc/news/list_18_3.html", "http://www.icoat.cc/news/list ...

  8. [Java多线程]-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  9. [LeetCode] 27. Remove Element ☆

    Given an array and a value, remove all instances of that value in place and return the new length. D ...

  10. Asp.Net MVC 自定义登录过滤器

    1.新建类BaseController用于统一所有控制器继承扩展,方便扩展登录等过滤器.示例如下: using CloudWave.JustBeHere.JBH_H5.Controllers.Attr ...