程序设计实践- Kernighan-克尼汉-裘宗燕译-高清-带书签.pdf
豆瓣评分9.1。程序设计实践- Kernighan-裘宗燕译。这本书从排错、测试、性能、可移植性、设计、界面、风格和记法等方面,讨论了程序设计中实际的、又是非常深刻和具有广泛意义的思想、技术和方法。这是一本牢牢占据经典书架最起眼位置的小册子,也是最符合KISS之道的神作之一。抛开已有的业界的best practices,这本书还修正了我记忆中的很多错误认识,Brian Kernighan、Rob Pike的大名自然是本书最好的招牌;但作者的求实态度更值得每一个认真的programmer学习。
整体上,全书采用一个实际问题作为切入点并且贯穿全书,涵盖了programming工作的各个方面,简明扼要却又发人深省。这个例子的选取本身就是非同谨慎的,规模不至于过大(100多行代码)但是又足以说明问题。
Chinaopub.coM
第1章风格
3
下载
elementArray = theel ementIndex
和
for (i=0; i nelms; i++)
elem<i>=i
人们常常鼓励程序员使用长的变量名,而不管用在什么地方。这种认识完全是错误的,清晰
性经常是随着简洁而来的。
现实中存在许多命名约定或者本地习惯。常见的比如:指针采用以p结尾的变量名,例如
node;全局变量用大写开头的变量名,例如G1obal;常量用完全由大写字母拼写的变量
名,如 CONSTANTS等。有些程序设计工场采用的规则更加彻底,他们要求把变量的类型和用
途等都编排进变量名字中。例如用pch说明这是一个字符指针,用 strO和 strfrom表示它
们分别是将要被读或者被写的字符串等。至于名字本身的拼写形式,是使用 pending或
upeNding还是 num pending,这些不过是个人的喜好问题,与始终如一地坚持一种切
合实际的约定相比,这些特殊规矩并不那么重要
j名约定能使自己的代码更容易理解,对别人写的代码也是一样。这些约定也使人在写
代码时更容易决定事物的命名。对于长的程序,选择那些好的、具有说明性的、系统化的名
字就更加重要。
C++的名字空间和Java的包为管理各种名字的作用域提供了方法,能帮助我们保持名字
的意义清晰,又能避免过长的名字。
保持一致性。相关的东西应给以相关的名字,以说明它们的关系和差异。
除了太长之外,下面这个Java类中各成员的名字一致性也很差
class Userqueue i
int noofItemsInQ, frontofTheQueue, queue Capaci ty
A public int noofUsersInQueueO i.F
这里同一个词“队列( queue)"在名字里被分别写为Q、ρueue或 queue。由于只能在类型
Userqueue里访问,类成员的名字中完全不必提到队列,因为存在上下文。所以
queue queue Capacity
完全是多余的。下面的写法更好:
class UserQueue i
int nitems, front, capacity;
× public int nuserso t…}
为这时可以如此写
queue capaci ty++i
n queue users o;
这样做在清晰性方面没有仼何损失。在这里还有可做的事情。例如 Items和useτs实际是同一种
东西,同样东西应该使用一个概念
函数采用动作性的名字。函数名应当用动作性的动词,后面可以跟着名词
now date. getTimeo
putchar (\n')
对返回布尔类型值(真或者假)的函数命名,应该清楚地反映其返回值情况。下面这样的语句
if (checkoctal(c))
程序设计实践
Chinaopub.com
下载
是不好的,因为它没有指明什么时候返回真,什么时候返回假。而
if (isoctal(c))
就把事情说清楚了∶如果参数是八进制数字则返回真,否则为假。
要准确。名字不仅是个标记,它还携带着给读程序人的信息。误用的名字可能引起奇怪的程
序错误。
本书作者之一写过一个名为 sorta1的宏,并且发布使用多年,而实际上它的实现是错误的
# define i soca1)〔(c)>="0&&(c)="0,&&〔c)=bitoff &0x7;
有些结构似乎总是要引诱人们去滥用它们。运算符?:大概属于这一类:
child=c! LC&&! RC)?0: ( LC?RC: LC)
如果不仔细地追踪这个表达式的每条路径,就几乎没办法弄清它到底是在做什么。下面的形
式虽然长了一点,但却更容易理解,其中的每条路径都非常明显
if (lc ==0 & RC == o)
child=0:
else if (lc ==0
child rc:
else
child= lo
运算符?:适用于短的表达式,这时它可以把4行的if-else程序变成1行。例如这样
max= Ca>b)? a: b:
或者下面这样
printf( the list has %d item%s\n",n, n==1? : 5")
第1章风格
7
下载
但是它不应该作为条件语句的一般性替换。
清晰性并不等同于简短。短的代码常常更凊楚,例如上面移字位的例子。不过有时代码
长一点可能更好,如上面把条件表达式改成条件语句的例子。在这里最重要的评价标准是易
于理解。
当心副作用。像艹这一类运算符具有副作用,它们除了返回一个值外,还将隐含地改变变量
的值。副作用有时用起来很方便,但有时也会成为问题,因为变量的取值操作和更新操作可
能不是同时发生。C和C++对与副作用有关的执行顺序并没有明确定义,因此下面的多次赋
值语句很可能将产生错误结果
str= str
这样写的意图是给str中随后的两个位置赋空格值,但实际效果却要依赖于i的更新时刻,很可
能把str里的一个位置跳过去,也可能导致只对实际更新一次。这里应该把它分成两个语句
str=’’;
str[i++
下面的赋值语句虽然只包含一个增量操作,但也可能给出不同的结果
array= i
如果初始时i的值是3,那么数组元素有可能被设置成3或者4。
不仅增量和减量操作有副作用,IO也是一种附带地下活动的操作。下面的例子希望从标
准输入读入两个互相有关的数:
scanf("‰d‰",&yr,& profit);
这样做很有问题,因为在这个表达式里的一个地方修改了yr,而在另一个地方又使用它。这
样,除非yr的新取值与原来的值相同,否则 profit就不可能是正确的。你可能认为事
情依赖于参数的求值顺序,实际情况并不是这样。这里的问题是: scanf的所有参数都在函
数被真正调用前已经求好值了,所以& profit实际使用的总是yr原来的值。这种问题可
能在仼何语言里发生。纠正的方法就是把语句分解为两个:
scanf( %d",&yr)
scanf ( %d",&profit);
下面的练习里列举了各种具有副作用的表达式
练习1-4改进下面各个程序片段:
ifC!(c=
c=”Y"))
return;
length (length BUFSIZE)? length BUFSIZE;
flag=印1ag?0:1:
quote=(*1ne=")?1:0
if val & 1)
bit 1:
else
bit o
练习1-5下面的例子里有什么错?
8
程序设计实践
下载
nt read (int *ip)i
scanf(%d",ip)
return☆
insert(&graph , read (&val), read (&ch))
练习1-6列出下面代码片段在各种求值顺序下可能产生的所有不同的输出:
printf(%d %d\n", n++, n++);
在尽可能多的编译系统中试验,看看实际中会发生什么情况。
1.3一致性和习惯用法
致性带来的将是更好的程序。如果程序中的格式很随意,例如对数组做循环,一会儿
采用下标变量从下到上的方式,一会儿又用从上到下的方式;对字符串一会儿用 strcpy做复
制,一会儿又用for循环做复制;等等。这些变化就会使人很难看清实际上到底是怎么回事。
而如果相同计算的每次出现总是采用同样方式,任何变化就预示着是经过了深思熟虑,要求
读程序的人注意。
使用一致的缩排和加括号风格。缩排可以显示岀程序的结构,那么什么样的缩排风格最好
呢?是把花括号放在if的同一行,还是放在下面一行?程序员们常常就程序的这些编排形式
争论不休。实际上,特定风格远没有一致地使用它们重要。不要在这里浪费时间。应该取
种风格,当然作者希望是他们所采用的风格,然后一致地使用
应该在那些并不必须用花括号的地方都加上它们吗?与一般的圆括号一样,花括号也可以
用来消除歧义,但是在使代码更清晰方面的作用却不那么大。为了保持某种-致性,许多程序
员总在循环或if的体外面加花括号。当这里只有一个语句时,加花括号就不是必要的,所以
作者倾向于去掉它们。如果你赞成我们的方法,那么就要注意在必需的时候不要忽略了它们,
例如,在程序里需要解决“悬空的else( dangling else)”问题时。下面是这方面的一个例子:
if (month = FEb)I
if (year%4==0
if (day >29)
legal FALSE
eIse
if (day >28)
legal FALSE
这里的缩排方式把人搞糊涂了,实际上e1se隶属于行
if (day>29
代码本身也是错的。如果一个i紧接在另一个之后,那么请一定加上花括号:
if (month = FEB)i
if (year%4 == 0t
if (day >29
Tegal =FALSE
F else i
f(day >28
legal= FALSE;
Chinaopub.com
第l章风格
载
语法驱动的编辑工具可以帮助避免这类错误
虽然上面程序里的错误已经修正,但这个结果代码还是很难懂。如果我们用一个变量保
存二月的天数,计算过程就很容易看明白了:
f(month = FEB)i
int nday
nday = 28;
if (year%4== 0)
nday =29
if (day >nday)
legal= False
这段代码实际上还是错的—2000年是闰年,而1900和2100都不是。要把现在这个结构改正
确是非常容易的。
此外,如果你工作在一个不是自己写的程序上,请注意保留程序原有的风格。当你需要
做修改时,不要使用你自己的风格,即使你特别喜欢它。程序的一致性比你本人的习惯更重
要,因为这将使随你之后的其他人生活得更容易些。
为了一致性,使用习惯用法。和自然语言一样,程序设计语言也有许多惯用法,也就是那些
经验丰富的程序员写常见代码片段的习惯方式。在学习一个语言的过程中,一个中心问题就
是逐渐熟悉它的习惯用法。
常见习惯用法之一是循环的形式。考虑在C、C++和Java中逐个处理n元数组中各个元素
的代码,例如要对这些元素做初始化。有人可能写出下面的循环
while (i =0;)
array<i>= 1.0
所有这些都正确,而习惯用法的形式却是
for (i =0: i 6666666666666 感谢分享!! 666666666666666666666666666 谢谢分享 学习下 666, thanks 66666666666 可以下载 :)好好学习。。。666 非常好,顶一下