最近在重温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. BZOJ3524 & LOJ2432:[POI2014]代理商Couriers——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=3524 https://loj.ac/problem/2432 给一个长度为n的序列a.1≤a[i] ...

  2. NOIP模拟

    1.要选一个{1,2,...n}的子集使得假如a和b在所选集合里且(a+b)/2∈{1,2,...n}那么(a+b)/2也在所选集合里 f[i]=2*f[i-1]-f[i-2]+g[i] g[n]:选 ...

  3. poj 1945 Power Hungry Cows A*

    Description:     就是给你一个数,你可以把它自乘,也可以把他乘或除以任意一个造出过的数,问你最多经过多少次操作能变换成目标数 思路:这题真的不怎么会啊.n = 20000,每一层都有很 ...

  4. shell函数使用

    函数定义格式: 和js有点类似,不过在 shell 中 function 关键字是可选的. 如: sum { // shell 语句 } function sum() { // shell 语句 } ...

  5. UVA-1635 数学

    UVA-1635 题意: 给定n个数a1,a2,a3.....an,依次求出相邻的两个数的和,最后成为一个数,问这个数模m的值与那些最初的数无关 例:a1,a2,a3, m=2 => a1+a2 ...

  6. git 从新的git 库中拉取---变换git地址用;

    2.先删后加 git remote rm origin git remote add origin [url]----- example :   git remote add origin http: ...

  7. Android 菜单和上下文演示

    在Action添加个TextView控件(上下文演示用); package com.example.test; import android.app.Activity;import android.o ...

  8. LintCode 391: Count Of Airplanes

    LintCode 391: Count Of Airplanes 题目描述 给出飞机的起飞和降落时间的列表,用 interval 序列表示. 请计算出天上同时最多有多少架飞机? 样例 对于每架飞机的起 ...

  9. 【AtCoder】ARC092 D - Two Sequences

    [题目]AtCoder Regular Contest 092 D - Two Sequences [题意]给定n个数的数组A和数组B,求所有A[i]+B[j]的异或和(1<=i,j<=n ...

  10. UIWebView---iOS-Apple苹果官方文档翻译

    CHENYILONG Blog UIWebView---iOS-Apple苹果官方文档翻译 UIWebView 技术博客http://www.cnblogs.com/ChenYilong/ 新浪微博h ...