最近在重温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. WebLogic XMLDecoder反序列化漏洞(CVE-2017-10271)复现

    WebLogic XMLDecoder反序列化漏洞(CVE-2017-10271)                                                -----by  ba ...

  2. 【hdu4035】Maze

    Portal --> hdu4035 Solution 讲道理不是很懂为啥概d那么喜欢走迷宫qwq (推式子推的很爽的一题?) 首先大力dp列式子 用\(f[i]\)表示从\(i\)到离开的期望 ...

  3. laravel 5.1 Eloquent常见问题

    1.新增一条记录以及判断是否新增成功 $instance = XxxModel::create(['a' => 1, 'b' => 2]); if ($instance->exist ...

  4. C++实现人员信息管理系统模拟

    利用C++语言实现基本的学生信息管理系统: 要求: 1-设置管理员密码 2-人员数据有:姓名,性别等基本的信息 3-可以添加,删除,保存,统计 #include<iostream> #in ...

  5. 111.保留的编号(ing)

    用于做整理的编号,000,111,222,……999

  6. UVA-10375 数学

    UVA-10375 题意 : 输入p,q,r,s,求C(p,q)/C(r,s). p,q,r,s<=10000:结果不超过1e8 代码: //显然不能直接计算,考虑每个数都可以由若干个素数乘积得 ...

  7. linux shell学习五

    参考:https://www.linuxdaxue.com/ Shell函数 因为函数是脚本类语言,在执行时是逐行执行的,因此,Shell 函数必须先定义后使用. Shell 函数的定义格式如下: [ ...

  8. Python urllib urlretrieve函数解析

    Python urllib urlretrieve函数解析 利用urllib.request.urlretrieve函数下载文件 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考文献 Ur ...

  9. centos7 nginx开启启动

    centos 7以上是用Systemd进行系统初始化的,Systemd 是 Linux 系统中最新的初始化系统(init),它主要的设计目标是克服 sysvinit 固有的缺点,提高系统的启动速度.关 ...

  10. 图:centrality

    [定义]Centrality:图中每个节点v的相对重要度c(v),重要度是什么可根据具体应用定义. [估计方法] Degree centrality Betweenness centrality Cl ...