《CCF中学生计算机程序设计》错误集合

《CCF中学生计算机程序设计》错误集合

头图与正文无关

《CCF中学生程序设计》教程是€€£的官方OI教程,目前有《入门篇》、《基础篇》、《提高篇》。其中里面有不少错误,为了避免误导读者,需要进行更正。

当然这些错误只是我的见解,如果有不同意见都可以讨论。

入门篇

(2016年11月第一版,2017年10月第9次印刷)

P15

(5)调试程序尽量不依赖调试工具

评:这句话很奇怪啊。

如果不使用类似于gdb之类的调试工具,那就只能静态查错和输出中间变量了。

对于复杂的程序这么做是很麻烦的,希望得到进一步的解释

P19

(4)区分大小写,例如1A和1a是两个不同的变量

评:都说了变量名不能以数字开头。也许他是想表达L的小写字母,但是字形不一样啊。

P20

float …… 数据类型为浮点数……(7位有效数字)

评:float 是浮点数,但是应该说清楚是单精度浮点数,以免和double混淆。

IEEE单精度标准下尾数是23位,2^23=8388608,一共七位,因此无法表示所有的7位有效数字,因此最多只能保证6位有效数字。

如果要求精度到7位,用float是不行的。

P24

程序中a+=b等效于a=a+b,但是前者执行速度比后者快

评:现在的编译器也不是弱智了。

写出两个对比程序,使用gcc 4.8.4分别汇编,没有增加任何编译优化选项。

可以发现两种程序的汇编语言是 完 全 一 致 的。

P24

交换两个变量,可以x+=y;y=x-y;x-=y;

评:看似精巧实则不准确。

这种交换方式必须限定不会溢出,而且必须是可加可减的数字。

然而题目只是要求“实现两个变量x、y之间的交换”。

如果你要交换一对 对象、字符串之类的,那肯定是不行的,这种方法不符合题目要求。

补充:根据评论区的说法,整数类型这么做即使溢出也没关系(仅限unsigned)。signed的溢出是UB,最后结果怎么样都还是个问题。

更不用说如果是两个数量级差距较大的实数这么做,那妥妥的失去精度啊!

(有同学指出)这种方式简直是以时间换O(1)的空间,速度还慢,除了华丽的炫技实际上没有任何的作用。

P33

数据类型转换……float->double

评:不是……怎么突然冒出一个double了啊,完全没有说明它是干什么用的耶!这里应该是double第一次闪亮登场……

结果我是谁?我是干什么的?数据范围有什么不同?和float有什么不同?一切都没说。

你当学生都能建立空中楼阁了嘛?还是认为读者可以明白没有讲过的知识点?

P34

long long

评:哦这边终于说了long long范围比 int 大。然后呢?范围多少?空间呢?

P44

%f,%lf 以小数形式输出单、双精度实数

评:先不提这边是全本书第一次提到“单精度”、“双精度”这两个词语(老老实实用float,double不好吗)

根据C++98标准,输出double时是不用%lf而是%f的。

补充:其实用gcc,即使是C++98用%lf也没啥问题啦。但是不符合标准就是不符合标准。

P53

使用rand()函数返回[0,MAX)之间的随机整数,其中MAX由所定义的数据类型而定

评:rand() 函数的范围上限的常量是RAND_MAX而不是什么MAX。

常量还跟“定义的数据类型”有关?有趣。常量本身就是固定数据类型的,它的值只和系统有关。

P53

要取得[a,b)的随机整数,使用rand()%(b-a)+a。
……

评:rand的最大值也就是RAND_MAX,在windows下这个值经常是32767,相当小。即使rand()的每个数字概率是相等的,这个生成的随机数的各个元素是不相等的。

尤其b-a比较大(比如大于16383)的时候,这种不均等概率使得两个数字得到的概率,甚至可以达到2:1。

P57

各种数据类型的列表
long double的范围是 3.4E-4932~1.1E+4932,占用12字节

评:哦终于有了各个类型集合表了,但是有效范围还是不准确啊!除此之外,你指数的上限写了,下限呢?

long double这种没有明确规定的东西你也敢拿出来说?就说这精度你这只有正数,那负数上哪去呢?

在标准中,没有规定long double的精度,只要不小于double就好。在不同系统上他们的精度是不同的。

P64

`a´<´b´

评:这个鬼畜的引号是啥?

P64

买水瓶,3元1个,买8送1
b=(cup-cup/8)*3

评:如果我就要买8个呢?那不就是(8-8/8)*3吗?

兄弟啊,你的意思是我掏7个的钱就能得到8个?你这么聪明店家会把你打死的!

P80

输入的3个数……存在下面几种情况:
【图】

评:哦,输出c,b,a不是已经结束了呢?怎么还要判断是否?判断的依据是什么呢?不但错位,而且还画蛇添足!

P81

三变量排序代码

评:奇怪的码风!多重条件判断不但没有括号,缩进又很鬼畜,都不知道那一层对应那一层。读者写出这样的程序会使自己都迷惑的。

P89

恩格尔系数为 食物支出金额/总支出金额
50%~60%为温饱,40%~50%为小康
程序:
输入x和y
int n=100*x/y+0.5(四舍五入)
switch(n/10)
case 5:输出温饱
case 4:输出小康

评:先不说这个奇怪的范围吧,这个区间哪个开哪个闭啊?只说了大于等于60是贫穷……行吧,那就勉强当做左闭右开吧。

当输入数据为 50 101时,n=100*50/101+0.5=int(50.000495)=50。然后n/10=5。

好家伙,国家好不容易把恩格尔系数降到了49.5%,勉强达到了小康线,结果你还是输出温饱,公然否认国家的扶贫成果,其心可诛!

P93-P94

double d;
……
if(d==0)……

评:虽然结果可能没错,但是经过运算后浮点数最好不要直接比较相等而是比较绝对值eps差。

以及上面冷不丁出来的sqr()又是什么鬼?

以及程序中的输出和题目输出格式不一致。

P96

走路所需时间为t1=dis/3+27+23,骑车所需时间为t2=dis/1.2

评:听说你走路速度比骑车还快。然后你每次走路都要花50秒的时间绑鞋带。

P97

double dis;
double t1,t2;
cin>>dis;
t1=dis*2+(27+23)*6;
t2=dis*5;
……if(t1==t2)……

评:这里很明显是想试图将化除为乘,避免精度误差。

如果输入是整数的话,倒是一个好方法,但也得使用int类型而不是double。

然而如果输入是实数,这么做是脱裤子放屁。谁告诉你浮点数乘法是精确值,而除法因为除不尽就不是精确值了?

忘记了浮点数都是二进制存的吗?如果不是由2^i的和组成的所有浮点数,都不是精确值!

然后浮点数比较相等又来了。

P106

for(循环变量初始化;循环条件;循环变量增量)

评:不严谨。你全部使用全角符号描述也就算了。最后一项只能用来求循环变量增量?

准确的说法叫做“迭代表达式”,在每次循环后都运算一次。

难道我不用增加循环变量,比如取下一个链表项的指针,就不能在这边写上咯?

P109

没有初始化的变量会随机产生一个初值

评:“随机”、“产生”?局部变量没有初始化无非就是利用堆栈里面被指定的空间,原来内存空间里面数据是什么这个变量的值就是什么。万一之前的空间已经给你初始化了呢?

何来“随机”、“产生”?似乎说的像随机生成一个数字存进去一样。

P125

除了1和它本身不再有其他约数的数,就是质数。

评:好家伙,直接把定义改了!怕是陈景润要跳起来打人!

怕作者默认了“数”就是正整数或者自然数了,有趣。

而且照这么说,1也是质数咯?


未完待续
更新: zhuanlan.zhihu.com/p/13

编辑于 05-07

文章被以下专栏收录