《写给大家看的C语言书(第2版)》是邮电社图灵公司引进翻译的一本C语言入门书,这是一本垃圾书。搞不清图灵为什么引进了这样一本垃圾书。该书作者基本不懂得C编程技术,书中误导、错谬比比皆是。
  该书的附录B给出了一个21点游戏的代码,这是一个很糟糕的C程序,毛病很多,实在不足以为初学者以示范,当反面教材还差不多。考虑到其中的很多毛病初学者也有,所以拿来在此评析一番。
  首先,这段程序可读性极差,甚至可以说是乱作一团。连我这种阅读代码能力很强的人看起来都很费劲,初学者根本就没有可能看懂。
  乱作一团和可读性还不是一回事。可读性指的是代码晦涩难懂,乱作一团则是指不顾起码的编程规范。譬如代码没有基本的缩进

do {
ans = getAns("Hit or stand (H/S)?");
if ( ans = 'H' )
{ playerGetsCard(&numCards,cards,
playerPoints);
}
}while(ans != 'S' );

任意地截断语句或声明

do { initCardsScreen(cards,playerPoints,dealerPoints,
total, &numCards);
dealerGetsCard(&numCards,cards, dealerPoints);

  代码本来就够乱的了,又塞进了一大堆更乱更不规范的注释。这些注释其实是应该翻译过来的,但是很可惜译者偷懒并没有翻译,对于国内初学者来说,这就更加混乱。混乱的代码和混乱的注释纠结在一起,整个源程序惨不忍睹。

  为了不折磨大家眼球,下面以删除了注释的代码为基础评析。大家也可以顺便比较一下(http://ishare.iask.sina.com.cn/f/20886221.html),看看这些注释是不是起了很坏的作用。如果感觉这里删除了注释的代码更清楚一些,那么无疑原书上的注释是垃圾。

#include <stdio.h>
#include <time.h>
#include <ctype.h>
#include <stdlib.h>#define BELL '\a'
#define DEALER 0
#define PLAYER 1#define ACELOW 0
#define ACEHIGH 1int askedForName = 0;void dispTitle(void);
void initCardsScreen(int cards[52],int playerPoints[2],
int dealerPoints[2], int total[2],
int *numCards);
int dealCard(int * numCards,int cards[52]);
void dispCard(int cardDrawn,int points[2]);
void totalIt(int points[2],int tatal[2],int who);
void dealerGetsCard(int *numCards,int cards[52],
int dealerPoints[2]);
void playerGetsCard(int *numCards,int cards[52],
int playerPoints[2]);
char getAns(char mesg[]);
void findWinner(int total[2]);main()
{
int numCards;
int cards[52],playerPoints[2],dealerPoints[2],total[2];
char ans;do { initCardsScreen(cards,playerPoints,dealerPoints,
total, &numCards);dealerGetsCard(&numCards,cards, dealerPoints);
printf("\n");
playerGetsCard(&numCards,cards,playerPoints);
playerGetsCard(&numCards,cards,playerPoints);
do {
ans = getAns("Hit or stand (H/S)?");
if ( ans = 'H' )
{ playerGetsCard(&numCards,cards,
playerPoints);
}
}while(ans != 'S' );
totalIt(playerPoints,total,PLAYER);
do{
dealerGetsCard(&numCards,cards,dealerPoints);
}while (dealerPoints[ACEHIGH] < 17 );
totalIt(dealerPoints,total,DEALER);
findWinner(total);
ans=getAns("\nPlay again(Y/N)?");  }while(ans=='Y');return ;}
void initCardsScreen(int cards[52],int playerPoints[2],
int dealerPoints[2], int total[2],
int *numCards)
{
int sub,val = 1 ;
char firstName[15];
*numCards=52;for(sub=0;sub<=51;sub++){
val = (val == 14) ? 1 : val;
cards[sub] = val;
val++;  }
for(sub=0;sub<=1;sub++)
{ playerPoints[sub]=dealerPoints[sub]=total[sub]=0;}
dispTitle();
if (askedForName==0)
{ printf("What is your first name?");
scanf(" %s",firstName);
askedForName=1;
printf("Ok, %s,get ready for casino action!\n\n",firstName);
getchar();
}
return;
}void playerGetsCard(int *numCards,int cards[52],
int playerPoints[2])
{
int newCard;
newCard = dealCard(numCards, cards);
printf("You draw:");
dispCard(newCard,playerPoints);
}
void dealerGetsCard(int *numCards,int cards[52],
int dealerPoints[2])
{
int newCard;
newCard = dealCard(numCards,cards);
printf("The dealer draws:");
dispCard(newCard,dealerPoints);
}
int dealCard(int * numCards,int cards[52])
{
int cardDrawn,subDraw;
time_t t;
srand(time(&t));
subDraw = (rand()%(*numCards));
cardDrawn = cards[subDraw];
cards[subDraw] = cards[*numCards -1];
(*numCards)-;
return cardDrawn;
}
void dispCard(int cardDrawn, int points[2])
{
switch(cardDrawn){
case(11): printf("%s\n","Jack");
points[ACELOW] += 10;
points[ACEHIGH] += 10;
break;
case(12): printf("%s\n","Queen");
points[ACELOW] += 10;
points[ACEHIGH] += 10;
break;
case(13): printf("%s\n","King");
points[ACELOW] += 10;
points[ACEHIGH] += 10;
break;
default : points[ACELOW] += cardDrawn;
if(cardDrawn==1)
{ printf("%s\n","Ace");points[ACEHIGH]+= 11;
}
else
{  points[ACEHIGH]+=cardDrawn;printf("%d\n",cardDrawn); }
}
return ;
}
void totalIt(int points[2],int total[2],int who)
{
if ((points[ACELOW] == points[ACEHIGH])||
(points[ACEHIGH] > 21 ))
{ total[who] = points[ACELOW];}else
{ total[who] = points[ACEHIGH];}if (who == PLAYER )
{printf("You have a total of %d\n\n", total[PLAYER]);}
else
{printf("The house stands with a total of %d\n\n", total[DEALER]);}
return;
}
void findWinner(int total[2])
{
if ( total[DEALER] ==  21 )
{ printf("The house wins.\n");return ;}
if ( (total[DEALER] > 21) && (total[PLAYER] > 21))
{ printf("%s", "Nobody wins.\n");return ; }
if ((total[DEALER] >= total[PLAYER])&& (total[DEALER] < 21))
{ printf("The house wins.\n");return ; }
printf("%s%c","You win!\n",BELL);
return;
}
char getAns(char mesg[])
{
char ans;
printf("%s", mesg);
ans = getchar();
getchar();
return toupper(ans);
}
void dispTitle(void)
{
int i = 0 ;
while(i<25)
{ printf("\n");
i++; }
printf("\n\n*Step right up to the Blackjack tables*\n\n");
return ;
}

View Code

  然而尽管去除了脏、乱、差的注释,代码依然很糟糕,例如

{ printf("\n");
i++; }

  更拙劣的是这个

{  points[ACEHIGH]+=cardDrawn;printf("%d\n",cardDrawn); }
}

  注意到中间有个小三(“}”)吗?这风格简直丑疯了,只好再整理一下。

#include <stdio.h>
#include <time.h>
#include <ctype.h>
#include <stdlib.h>#define BELL '\a'
#define DEALER 0
#define PLAYER 1#define ACELOW 0
#define ACEHIGH 1int askedForName = 0;void dispTitle(void);
void initCardsScreen(int cards[52],int playerPoints[2],
int dealerPoints[2], int total[2],
int *numCards);
int dealCard(int * numCards,int cards[52]);
void dispCard(int cardDrawn,int points[2]);
void totalIt(int points[2],int tatal[2],int who);
void dealerGetsCard(int *numCards,int cards[52],
int dealerPoints[2]);
void playerGetsCard(int *numCards,int cards[52],
int playerPoints[2]);
char getAns(char mesg[]);
void findWinner(int total[2]);main()
{int numCards;int cards[52],playerPoints[2],dealerPoints[2],total[2];char ans;do { initCardsScreen(cards,playerPoints,dealerPoints,total, &numCards);dealerGetsCard(&numCards,cards, dealerPoints);printf("\n");playerGetsCard(&numCards,cards,playerPoints); playerGetsCard(&numCards,cards,playerPoints);do{ans = getAns("Hit or stand (H/S)?");if ( ans == 'H' ){ playerGetsCard(&numCards,cards,playerPoints);}  }while( ans != 'S' );totalIt(playerPoints,total,PLAYER);do{dealerGetsCard(&numCards,cards,dealerPoints);}while (dealerPoints[ACEHIGH] < 17 );totalIt(dealerPoints,total,DEALER);findWinner(total); ans = getAns("\nPlay again(Y/N)?");  }while(ans=='Y');return ;}void initCardsScreen( int cards[52],int playerPoints[2],int dealerPoints[2], int total[2], int *numCards )
{int sub,val = 1 ;char firstName[15];*numCards=52;for(sub=0;sub<=51;sub++){val = (val == 14) ? 1 : val;cards[sub] = val;val++;  }for(sub=0;sub<=1;sub++){ playerPoints[sub]=dealerPoints[sub]=total[sub]=0;}dispTitle();if (askedForName==0){ printf("What is your first name?");scanf(" %s",firstName);askedForName=1;printf("Ok, %s,get ready for casino action!\n\n",firstName);getchar();}return;
}void playerGetsCard(int *numCards,int cards[52],int playerPoints[2])
{int newCard;newCard = dealCard(numCards, cards);printf("You draw:");dispCard(newCard,playerPoints);
}void dealerGetsCard(int *numCards,int cards[52],int dealerPoints[2])
{int newCard;newCard = dealCard(numCards,cards);printf("The dealer draws:");dispCard(newCard,dealerPoints);
}int dealCard(int * numCards,int cards[52])
{int cardDrawn,subDraw;time_t t;srand(time(&t));subDraw = (rand()%(*numCards));cardDrawn = cards[subDraw];cards[subDraw] = cards[*numCards -1];(*numCards)-;return cardDrawn;
}void dispCard(int cardDrawn, int points[2])
{switch(cardDrawn){case(11): printf("%s\n","Jack");points[ACELOW] += 10;points[ACEHIGH] += 10;break;case(12): printf("%s\n","Queen");points[ACELOW] += 10;points[ACEHIGH] += 10;break;case(13): printf("%s\n","King");points[ACELOW] += 10;points[ACEHIGH] += 10;break;default : points[ACELOW] += cardDrawn;if(cardDrawn==1){ printf("%s\n","Ace");points[ACEHIGH]+= 11;}else{  points[ACEHIGH]+=cardDrawn;printf("%d\n",cardDrawn); }}return ;
}void totalIt(int points[2],int total[2],int who)
{if ( (points[ACELOW] == points[ACEHIGH])||(points[ACEHIGH] > 21 )){ total[who] = points[ACELOW];}else{ total[who] = points[ACEHIGH];}if (who == PLAYER ){printf("You have a total of %d\n\n", total[PLAYER]);}else{printf("The house stands with a total of %d\n\n", total[DEALER]);}return;
}void findWinner(int total[2])
{if ( total[DEALER] ==  21 ){printf("The house wins.\n");return ;}if ( (total[DEALER] > 21) && (total[PLAYER] > 21) ){ printf("%s", "Nobody wins.\n");return ; }if ((total[DEALER] >= total[PLAYER])&& (total[DEALER] < 21)){ printf("The house wins.\n");return ; }printf("%s%c","You win!\n",BELL);return;
}char getAns(char mesg[])
{char ans;printf("%s", mesg);ans = getchar();getchar();return toupper(ans);
}void dispTitle(void)
{int i = 0 ;while(i<25){ printf("\n");i++; }printf("\n\n*Step right up to the Blackjack tables*\n\n");return ;
}

View Code

  这回勉强能看得下去了。现在不难发现main()中

return ;

  实际上是

 return 0;

之误。
  在dealCard()函数中,也存在一个明显的错误

 (*numCards)-;

  实际上应该是

(*numCards)--;

 --*numCards;

 现在来阅读代码。首先看到的是

int askedForName = 0;

  这个外部变量看起来很别扭。从21点这个问题来看,怎么也不会想到程序会需要这样一个名为askedForName 的变量,对于整个问题来讲,这个变量是无关痛痒的鸡毛蒜皮。把这种鸡毛蒜皮性质的变量提升到外部变量这样重要的代码地位,就如同把家里一件可能几十年还用不到一次的小东西布置在客厅显眼位置一样荒唐。

  那么作者为什么要设置这个外部变量呢?估计有两种可能,一种是缺乏足够的代码写作技能,某段代码实在写不下去了,无可奈何地用了一个外部变量。另一种情况是写代码之前缺乏充分缜密的构思,思路有漏洞,写到中间时才发现,只好用这种办法来补救。后一种情况比前一种更糟糕。这就像盖房子一样,盖到中间才发现继续盖下去,房子的一个墙角就会盖到水塘里一样,之后匆忙修改图纸,房子无法按原计划形状盖成只好不顾了。
  实际上这时应该重新思考,重写代码,而不是修修补补。

(未完待续)

劣质代码评析——《写给大家看的C语言书(第2版)》附录B之21点程序(一)相关推荐

  1. 劣质代码评析——《写给大家看的C语言书(第2版)》附录B之21点程序(三)

    下面来考察一下main()的总体结构. 29. main() 30. { 31. int numCards; 32. int cards[52],playerPoints[2],dealerPoint ...

  2. 劣质代码评析——《写给大家看的C语言书(第2版)》附录B之21点程序(二)

    下面继续分析这个代码.为了便于说明问题,为代码添加了行号. 0. #include <stdio.h> 1. #include <time.h> 2. #include < ...

  3. 写给大家看的Web设计书:第3版(世界级设计大师指点迷津)(全彩印刷)

    写给大家看的Web设计书:第3版(世界级设计大师指点迷津)(全彩印刷) 基本信息 原书名: The Non-Designer's Web Book, 3rd Edition 原出版社: Peachpi ...

  4. 代码是写给人看的,请C/C++过来的程序员们多学习软件工程

    博客园新闻里面有个<让人抓狂的代码> http://news.cnblogs.com/n/156507/#bottom 里面的多数都是同意的. 但是其中第一条我是坚决反对的. 1. 确保这 ...

  5. 代码是写给人看的还是写给机器看的?

    在大部分的情况下我会认为代码是写给人看的.虽然代码最后的执行者是机器,但是实际上代码更多的时候是给人看的.我们来看看一段代码的生命周期:开发 --> 单元测试 --> Code Revie ...

  6. 《写给大家看的Web设计书(第3版)》即将上市

    <写给大家看的设计书>姊妹篇--<写给大家看的Web设计书(第3版)> 即将上市.它是Robin Williams的又一本设计书. 也许大家对Robin Williams的畅销 ...

  7. 写给大家看的Web设计书

    以前自己做的界面或是展示的PPT,总觉得不协调,生搬硬套.也许看看设计方面的书能有所改善. <写给大家看的Web设计书>,面向的是非技术的设计人员,图例丰富,内容受用,读起来很愉快. 基本 ...

  8. 读书笔记—写给大家看的PPT设计书

    作者:[美]Robin Williams 第一部分 写在设计之前 在现实生活中你可以表现得不可思议地愚蠢,你的话可以让人听得毫无兴致,昏昏欲睡,但是在这个演讲的舞台上,你却是一个明星!同时你还承担着让 ...

  9. 《写给大家看的Web设计书》读书笔记

    这好像是一个系列的书,都叫<写给大家看的*设计书>,不过感觉这本书介绍的东西有点老,不过排版还是很精致的,老外做东西还是认真. 这里只想记录一下设计的原则,其实以前在别的书中也看过,不过怎 ...

最新文章

  1. SpringMVC总结帖
  2. 从零开始学习jQuery (九) jQuery工具函数 【转】
  3. Hibernate事实:如何“断言” SQL语句计数
  4. 真彩色图像数据量 计算_免费深度学习实战:高效训练及加速推理,送英特尔神经计算棒 2 代 (报名·深圳)...
  5. 完整版linux下android源码下载、编译、模拟器启动运行
  6. 使用jsBridge实现H5与原生App交互
  7. 城轨车辆段联锁设备采用计算机联锁,车辆段计算机联锁设备
  8. 我的Android进阶之旅------android中一些特殊字符(如:←↑→↓等箭头符号)的Unicode码值
  9. Mybatis之分页插件PageHelper工作原理
  10. python按任意键退出_python按任意键继续程序
  11. A ArrayLink for JavaME
  12. Unity初级(十二)
  13. c语言opencv所用库函数,初窥Opencv
  14. Google Play支付 接入配置
  15. SPS PDSCH的HARQ反馈
  16. Lora无线模块在畜牧业中的应用
  17. python二级证书含金量排名_计算机二级证书的含金量不高?别小瞧,这些优势用处不小!...
  18. 《算法导论》CLRS算法C++实现(十一)P163 红黑树
  19. 任何值得去的地方,都没有捷径。
  20. 『 图片处理 』图片转换字符图

热门文章

  1. new 数组_编程-遍历数组元素N次/填充数组至指定长度
  2. 计算机竞赛CCC可以直接学吗,CCC 计算机竞赛到底有多牛!
  3. 锐捷交换机配置snmp版本_snmp交换机配置
  4. mysql下载解压安装_mysql zip 解压安装
  5. python参数类型限定_python限定方法参数类型、返回值类型、变量类型等|python3教程|python入门|python教程...
  6. 使用version遇到的那些坑
  7. 视频录制,压缩实现源码
  8. 5-flutter 布局和列表
  9. 【一步步学小程序】1.创建项目以及TabBar
  10. iOS通过CAShapeLayer和UIBezierPath画环形进度条