K&R《C语言》书中的一个Bug
最近在重温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的更多相关文章
- 学习K&R时初学者经常遇到的一个问题——EOF
学习K&R时初学者经常遇到的一个问题——EOF
- C++primer原书中的一个错误(派生类using声明对基类权限的影响)
在C++primer 第4版的 15章 15.2.5中有以下这样一段提示: "注解:派生类能够恢复继承成员的訪问级别,但不能使訪问级别比基类中原来指定的更严格或者更宽松." 在vs ...
- 微软BI 之SSIS 系列 - MVP 们也不解的 Scrip Task 脚本任务中的一个 Bug
开篇介绍 前些天自己在整理 SSIS 2012 资料的时候发现了一个功能设计上的疑似Bug,在 Script Task 中是可以给只读列表中的变量赋值.我记得以前在 2008 的版本中为了弄明白这个配 ...
- 记录一个使用HttpClient过程中的一个bug
最近用HttpClient进行链接请求,开了多线程之后发现经常有线程hang住,查看线程dump java.lang.Thread.State: RUNNABLE at java.net.Socket ...
- yarn client中的一个BUG的修复
org.apache.spark.deploy.yarn.Client.scala中的monitorApplication方法: /** * Report the state of an applic ...
- python日志轮转RotatingFileHandler在django中的一个bug
简介 大量过时的日志会占用硬盘空间,甚至长时间运行不注意会占满硬盘导致宕机,那么就可以使用内建logging模块根据文件大小(logging.handlers.RotatingFileHandler) ...
- 关于ligerUI中ligerTree代码中的一个bug,造成该控件无法通过url的POST方式加载数据
该bug造成ligerTree参数中的method无论你怎么设置都只能用get方式提交 由于本人水平有限,只是找到原因,但无法修正 ligerUI v1.1.9 版本中的ligerui.all.js文 ...
- tensorflow代码中的一个bug
tensorflow-gpu版本号 pip show tensorflow-gpu Name: tensorflow-gpu Version: 1.11.0 Summary: TensorFlow i ...
- 【紫书】BigInteger 高精度类型 原书上有一个bug:A+B!=B+A
存个代码 struct BigInterger { static const int BASE = 1e8; ; vector<int> s; BigInterger() { *this ...
随机推荐
- 洛谷 P3398 仓鼠找sugar 解题报告
P3398 仓鼠找sugar 题目描述 小仓鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每个节点的编号为1~n.地下洞穴是一个树形结构.这一天小仓鼠打算从从他的卧室(a)到餐厅(b),而 ...
- CDN公共库、前端开发常用插件一览表(VendorPluginLib)
=======================================================================================前端CDN公共库===== ...
- [JSOI2008]Blue Mary的战役地图——全网唯一一篇dp题解
全网唯一一篇dp题解 网上貌似全部都是哈希+二分(反正我是大概baidu了翻了翻)(还有人暴力AC了的..) 哈希还是相对于dp还是比较麻烦的. 而且正确性还有可能被卡(当然这个题不会) 而且还容易写 ...
- 【hdu4035】Maze
Portal --> hdu4035 Solution 讲道理不是很懂为啥概d那么喜欢走迷宫qwq (推式子推的很爽的一题?) 首先大力dp列式子 用\(f[i]\)表示从\(i\)到离开的期望 ...
- 流媒体协议之RTSP客户端的实现20171014
RtspClient是基于jrtplib实现的,目前仅支持h264格式,后续将不断迭代优化,加入对其他格式的支持,并且将实现RTSP的服务端. RtspClient的功能是接收服务端过来流,然后写入到 ...
- 随机抽样一致性算法(RANSAC)示例及源代码--转载
转载自王先荣 http://www.cnblogs.com/xrwang/p/SampleOfRansac.html 作者:王先荣 大约在两年前翻译了<随机抽样一致性算法RANSAC>,在 ...
- poj 1273 裸 网络流 (dinic)
Description Every time it rains on Farmer John's fields, a pond forms over Bessie's favorite clover ...
- duilib 修复Text控件无法设置宽度的bug,增加自动加算宽度的属性
转载请说明原出处,谢谢~~: 今天有朋友反映CTextUI控件无法设置宽度,于是修复了这个bug,顺便给Text控件增加了一个自动计算宽度的属性,描述如下 <Attribute name=&qu ...
- ASP.NET Core的身份认证框架IdentityServer4--(5)自定义用户登录(通过接口登录,无UI版本)
官网接口详解文档地址:文档地址 (PS:可通过接口名称搜索相应接口信息.) 源码地址:https://github.com/YANGKANG01/IdentityServer4-IdentityAut ...
- IIC总线学习
IIC总线 IIC协议简要说明: 1.2条双向串行线,一条数据线称为SDA,一条时钟线SCL,双向半双工 2.传输的设备之间只是简单的主从关系,主机可以作为主机发送也可以作为主机接收,任何时候只能由一 ...