Java自学网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 12858|回复: 58

程序设计实践- Kernighan-克尼汉-裘宗燕译-高清-带书签.pdf

  [复制链接]

该用户从未签到

9

主题

182

帖子

363

积分

普通会员

Rank: 2

积分
363
发表于 2022-7-19 23:51:02 | 显示全部楼层 |阅读模式
豆瓣评分9.1。程序设计实践- Kernighan-裘宗燕译。这本书从排错、测试、性能、可移植性、设计、界面、风格和记法等方面,讨论了程序设计中实际的、又是非常深刻和具有广泛意义的思想、技术和方法。这是一本牢牢占据经典书架最起眼位置的小册子,也是最符合KISS之道的神作之一。: `1 y. r5 O* O5 x
4 _9 @* K9 U) ]/ n9 U8 g7 _$ G1 C* H# I
抛开已有的业界的best practices,这本书还修正了我记忆中的很多错误认识,Brian Kernighan、Rob Pike的大名自然是本书最好的招牌;但作者的求实态度更值得每一个认真的programmer学习。7 k' r* M. P. p5 d; U' d' J" A! O* a
& d* [1 O. t, k8 Z5 w% C
整体上,全书采用一个实际问题作为切入点并且贯穿全书,涵盖了programming工作的各个方面,简明扼要却又发人深省。这个例子的选取本身就是非同谨慎的,规模不至于过大(100多行代码)但是又足以说明问题。
. b1 i+ D8 y) \& s$ _! b1 iChinaopub.coM
5 M8 K8 m5 J3 t9 R8 _1 v- d第1章风格: J2 J3 `* l/ H' h
31 A; H* q- ]# Y- [7 a' ~' ~! w4 q  e
下载
. `& X9 \+ h. r9 NelementArray [theElementIndex]= theel ementIndex
+ M( C, x  m& `! \
. x9 a" b, }9 k9 X& wfor (i=0; i nelms; i++)
7 y! I4 i/ g' S2 h, w) `0 ^elem<i>=i0 A$ }* t# {+ a6 ~
人们常常鼓励程序员使用长的变量名,而不管用在什么地方。这种认识完全是错误的,清晰# Y$ ~! t/ R) ?) J3 J! B
性经常是随着简洁而来的。9 Q7 x! C4 U* v" N1 V
现实中存在许多命名约定或者本地习惯。常见的比如:指针采用以p结尾的变量名,例如
/ H/ F" s! E* i) B6 B, @2 A$ Anode;全局变量用大写开头的变量名,例如G1obal;常量用完全由大写字母拼写的变量" @! v8 w6 j( j- E; Z1 ^
名,如 CONSTANTS等。有些程序设计工场采用的规则更加彻底,他们要求把变量的类型和用
$ t2 a& ^) r0 f) E8 S  u9 D途等都编排进变量名字中。例如用pch说明这是一个字符指针,用 strO和 strfrom表示它0 y6 X! |, C) y$ j3 u, j1 _9 [* v
们分别是将要被读或者被写的字符串等。至于名字本身的拼写形式,是使用 pending或: n0 A& t6 e" g2 J& R9 U
upeNding还是 num pending,这些不过是个人的喜好问题,与始终如一地坚持一种切
% O% }# Z$ c8 P1 U: Q+ p" z( e7 y合实际的约定相比,这些特殊规矩并不那么重要
( E$ r1 ]! C$ n/ n* nj名约定能使自己的代码更容易理解,对别人写的代码也是一样。这些约定也使人在写
% P4 E' ]0 e' n1 V代码时更容易决定事物的命名。对于长的程序,选择那些好的、具有说明性的、系统化的名
/ K$ z  T# |" K; `) P字就更加重要。
! X% W% v$ \1 |; p3 ]; t# r: }C++的名字空间和Java的包为管理各种名字的作用域提供了方法,能帮助我们保持名字
) B/ d  s3 O# F* ^. N的意义清晰,又能避免过长的名字。7 A+ y  ?7 B+ d# X+ C
保持一致性。相关的东西应给以相关的名字,以说明它们的关系和差异。1 Q" J7 ^; |; Y1 `- T
除了太长之外,下面这个Java类中各成员的名字一致性也很差. p$ E5 U3 P" j# h7 t# U/ Y" w- @6 s
class Userqueue i3 y4 f/ m$ M6 N- N/ F: m9 e
int noofItemsInQ, frontofTheQueue, queue Capaci ty3 r& S7 L  m# E/ J0 r9 _: D0 M9 a# C
A public int noofUsersInQueueO i.F
$ J  q  B9 T7 }- U$ g5 ~这里同一个词“队列( queue)&quot;在名字里被分别写为Q、ρueue或 queue。由于只能在类型% a4 @- Z/ p7 K2 B1 y
Userqueue里访问,类成员的名字中完全不必提到队列,因为存在上下文。所以
( f1 Z" M. x3 Hqueue queue Capacity
! o3 N. O2 e; y" |% k  ?5 E) d完全是多余的。下面的写法更好:& H9 a2 s2 H7 h/ c9 L5 I# p$ d; |  S
class UserQueue i& B  n0 h# T! g7 G4 R! s. p
int nitems, front, capacity;6 v0 t; o) ^$ i# T, d+ y
× public int nuserso t…}. }" C8 U& X* q$ v( F. o
为这时可以如此写  S$ N4 g- d  V
queue capaci ty++i
4 @% p9 ~5 D7 E( X4 Q8 d; H, Qn queue users o;" b# d* [9 V% d+ r4 w) d% {
这样做在清晰性方面没有仼何损失。在这里还有可做的事情。例如 Items和useτs实际是同一种
) {0 j4 N9 G1 O- b& a0 Z9 T, ^. c东西,同样东西应该使用一个概念
- C$ f- C! Z9 m' B0 h函数采用动作性的名字。函数名应当用动作性的动词,后面可以跟着名词9 b2 \2 T* |  P; `& O
now date. getTimeo
9 S1 r0 P# W- T% Y& wputchar (\n')
. Y! ?5 s0 P1 g! x对返回布尔类型值(真或者假)的函数命名,应该清楚地反映其返回值情况。下面这样的语句' w- H. D4 e9 X' H( r
if (checkoctal(c))
; d7 `  Q7 w8 U2 b0 H8 [/ g0 }; l7 j6 V% d+ D" v: W# U0 U
程序设计实践+ f1 @1 q% J4 H1 Q" g9 L& J7 ~3 x
Chinaopub.com0 q, `7 o: w) D% _2 k3 j) P6 H
下载
$ f! |: m* W* v: G是不好的,因为它没有指明什么时候返回真,什么时候返回假。而$ w$ p3 H! O6 Y  u
if (isoctal(c))5 [" V! I+ r, \
就把事情说清楚了∶如果参数是八进制数字则返回真,否则为假。8 U2 x& V: j0 C, }: l
要准确。名字不仅是个标记,它还携带着给读程序人的信息。误用的名字可能引起奇怪的程% L* U7 Q4 C2 X" W2 Q0 ]
序错误。9 B5 P4 n" H3 F
本书作者之一写过一个名为 sorta1的宏,并且发布使用多年,而实际上它的实现是错误的
$ ?: X! k1 @: ?5 B# define i soca1)〔(c)>=&quot;0&&(c)=&quot;0,&&〔c)=bitoff &0x7;# x5 T+ \4 @: |  y- @$ |2 q
有些结构似乎总是要引诱人们去滥用它们。运算符?:大概属于这一类:% R# m4 m9 }/ {6 n: `6 A
child=c! LC&&! RC)?0: ( LC?RC: LC)9 A4 {* Q$ @" `4 ]$ F8 ]
如果不仔细地追踪这个表达式的每条路径,就几乎没办法弄清它到底是在做什么。下面的形8 ], }! H1 p; U2 g* T
式虽然长了一点,但却更容易理解,其中的每条路径都非常明显9 g0 {' J9 J! z
if (lc ==0 & RC == o)' p' {" @& G0 O) k/ J% t' ?( S
child=0:
) \6 |; Z' _- G: U2 l2 Velse if (lc ==0
" K4 O" R" b4 [5 a( a& E0 V7 [child rc:
) N5 ^# l* f6 p$ Lelse
/ _: g0 ?6 E3 {2 X; `child= lo
' p6 o; k7 h, D4 ?1 D% x运算符?:适用于短的表达式,这时它可以把4行的if-else程序变成1行。例如这样3 `( Q8 ]# f6 Y. A# z) g/ ]1 O
max= Ca>b)? a: b:  V4 b; B6 Q; e" d+ X& A6 ^
或者下面这样0 Y# |5 f$ l3 \2 [, @9 T
printf( the list has %d item%s\n&quot;,n, n==1? : 5&quot;)% A% a, g' J2 r2 v# x3 y

: u( d* n  B2 v) o第1章风格9 ^: P3 {9 e8 f. Q
78 R2 b+ M& p, F* F  }) ?* e2 ^
下载3 l) s$ [: \9 S# L; Q" ~* |
但是它不应该作为条件语句的一般性替换。
0 g- P" |% q* i& {7 m/ U0 |# D清晰性并不等同于简短。短的代码常常更凊楚,例如上面移字位的例子。不过有时代码
. j% S' o5 ]( N$ I长一点可能更好,如上面把条件表达式改成条件语句的例子。在这里最重要的评价标准是易
1 y  E# b: O/ N, a于理解。1 l" V" d. t7 \1 r# Q, U
当心副作用。像艹这一类运算符具有副作用,它们除了返回一个值外,还将隐含地改变变量
& G# d: S2 G- C9 h( A* g6 p! Q的值。副作用有时用起来很方便,但有时也会成为问题,因为变量的取值操作和更新操作可
1 a/ b4 \8 w/ ]% [' k, G能不是同时发生。C和C++对与副作用有关的执行顺序并没有明确定义,因此下面的多次赋- G, u, P: U  U7 N1 W; g
值语句很可能将产生错误结果
( t' a& x9 t$ A  ?9 w1 ?- J& V! t6 _str[i++]= str[i++]
, X9 n4 {9 U  B- i# i这样写的意图是给str中随后的两个位置赋空格值,但实际效果却要依赖于i的更新时刻,很可
6 a0 ^% d( H- v3 q  {能把str里的一个位置跳过去,也可能导致只对实际更新一次。这里应该把它分成两个语句* m; `& l/ Q8 Z+ {
str[i++]=’’;$ X5 j. R: f3 [. T" `- ~% V
str[i++* A! Q. A9 W' n3 T  c
下面的赋值语句虽然只包含一个增量操作,但也可能给出不同的结果: C/ R( z7 a) A$ e, F0 l
array[i++]= i# {; @4 _$ o0 N/ B3 x
如果初始时i的值是3,那么数组元素有可能被设置成3或者4。
' ^2 g3 ^  B% U- D% _5 F$ O不仅增量和减量操作有副作用,IO也是一种附带地下活动的操作。下面的例子希望从标* d5 _# ^: f: r- i- h7 N+ I; d
准输入读入两个互相有关的数:
2 P8 a' L; x& y7 L9 R1 `8 H( Uscanf(&quot;‰d‰&quot;,&yr,& profit[yr]);
0 U! O6 G7 c% P& W9 j这样做很有问题,因为在这个表达式里的一个地方修改了yr,而在另一个地方又使用它。这
! c( |' F) C; v% v8 ~3 Z样,除非yr的新取值与原来的值相同,否则 profit[yr]就不可能是正确的。你可能认为事
- W+ t# Q! V, X情依赖于参数的求值顺序,实际情况并不是这样。这里的问题是: scanf的所有参数都在函' L; ~) A2 _& ]. q2 T2 |8 m
数被真正调用前已经求好值了,所以& profit[yr]实际使用的总是yr原来的值。这种问题可/ z  Z( q; m. d
能在仼何语言里发生。纠正的方法就是把语句分解为两个:
1 l3 T9 s+ V. F, ]) R8 v% k0 V6 {9 Vscanf( %d&quot;,&yr)* H( o+ {$ }; j2 T
scanf ( %d&quot;,&profit[yr]);+ s8 G& s) K# d1 @9 Y+ m' w2 q5 E
下面的练习里列举了各种具有副作用的表达式, b3 A1 `" Y' ]/ s
练习1-4改进下面各个程序片段:: U( g) F* m' @
ifC!(c=
8 {% b( _* w. G. Bc=”Y&quot;))
, H1 A' `7 C0 ]return;, U9 V6 p# R. V- d# d/ A/ |" V- I4 k2 w
length (length BUFSIZE)? length BUFSIZE;! [- E+ J+ O! k1 Y* ]
flag=印1ag?0:1:5 \! d- d. k6 a; I* C$ ^
quote=(*1ne=&quot;)?1:0
7 |; }& o  f) ]% rif val & 1)7 O0 r1 C  B( {5 [+ }: Z2 J
bit 1:1 U# S8 l6 u% h/ f5 t+ B6 V$ F! F
else8 H8 e% B+ @0 s7 U1 b' W7 T* z" r
bit o/ k: V  E$ p8 Q+ e: m% n
练习1-5下面的例子里有什么错?/ ?) ^* l  Z3 O# s* }

- w, ]; }0 S$ j8
% \- q, Y6 h  ^程序设计实践
7 ]3 K. S5 w/ V+ d1 Y! |$ _- ]7 H下载
- ~: u& P6 I5 L! m6 l4 L9 Hnt read (int *ip)i
6 w0 T" u# a6 p' F% s% Sscanf(%d&quot;,ip)8 O0 v; d7 H- j" I; O# }& B
return☆* w/ e$ q3 p0 V
insert(&graph [vert], read (&val), read (&ch))
: I. t% O" Q: c& P. t/ h$ b, P4 a练习1-6列出下面代码片段在各种求值顺序下可能产生的所有不同的输出:
, |. E5 @# B3 U: xprintf(%d %d\n&quot;, n++, n++);
4 F% M! n) R9 w, J8 e在尽可能多的编译系统中试验,看看实际中会发生什么情况。
2 r' k. h1 o/ y8 V: K1.3一致性和习惯用法! f% i, q' b( y
致性带来的将是更好的程序。如果程序中的格式很随意,例如对数组做循环,一会儿
6 T9 q# p2 g) H  j6 V5 k# s' u1 v采用下标变量从下到上的方式,一会儿又用从上到下的方式;对字符串一会儿用 strcpy做复
! q/ R# G8 F1 n3 t- X制,一会儿又用for循环做复制;等等。这些变化就会使人很难看清实际上到底是怎么回事。
  n1 D6 ?* y9 ~4 e$ ^而如果相同计算的每次出现总是采用同样方式,任何变化就预示着是经过了深思熟虑,要求
# I; c. E# p2 m1 R读程序的人注意。
  f1 X; w: L* d6 }3 T: N, x使用一致的缩排和加括号风格。缩排可以显示岀程序的结构,那么什么样的缩排风格最好
: |3 B) ^: ~$ J6 H: y呢?是把花括号放在if的同一行,还是放在下面一行?程序员们常常就程序的这些编排形式$ p- x" o+ F! p5 k
争论不休。实际上,特定风格远没有一致地使用它们重要。不要在这里浪费时间。应该取! z% n; n2 [3 w+ y
种风格,当然作者希望是他们所采用的风格,然后一致地使用
+ ^: _5 T( q( w应该在那些并不必须用花括号的地方都加上它们吗?与一般的圆括号一样,花括号也可以" M8 h, S7 ]  B  m% ^! |# Z& T
用来消除歧义,但是在使代码更清晰方面的作用却不那么大。为了保持某种-致性,许多程序4 y# {* I, |4 n+ w8 y
员总在循环或if的体外面加花括号。当这里只有一个语句时,加花括号就不是必要的,所以* Y* a& r0 `; G2 s( z) Y5 ~. [
作者倾向于去掉它们。如果你赞成我们的方法,那么就要注意在必需的时候不要忽略了它们,
, W6 E3 _) M2 G$ j( ]/ E, {例如,在程序里需要解决“悬空的else( dangling else)”问题时。下面是这方面的一个例子:
: {7 H% p# n! k; T8 cif (month = FEb)I
' G3 s; u& c6 Q/ M, Gif (year%4==0
  Y& g6 K, @: N3 Yif (day >29)/ p2 [2 S; k* d7 v
legal FALSE8 Q( H- k+ v+ a. U
eIse, I' C( M9 x0 }6 Q% S+ ~
if (day >28)
; }2 V/ V+ `- U+ W) }0 }legal FALSE
# Q0 d5 e9 e1 D! Y& n0 x* f- W这里的缩排方式把人搞糊涂了,实际上e1se隶属于行% G( y, m% J! x
if (day>29
( k  G3 C2 A4 ~# z代码本身也是错的。如果一个i紧接在另一个之后,那么请一定加上花括号:2 r3 Y' z8 N% I! E7 b0 v2 R
if (month = FEB)i
. |3 \( ]* Q1 \# `9 f/ s& T" f( Zif (year%4 == 0t. X" S2 _, B7 m' _
if (day >29) U! j; d+ G: \1 v
Tegal =FALSE
. I' V7 Z/ z* TF else i
: t  {& b+ a* }& t* A" S* x2 s3 Cf(day >28
- w7 A; P& t, C1 olegal= FALSE;
7 t4 V( D7 M- C- [6 B0 F7 X7 w6 R8 g  M7 Q6 h% t  Y- l
Chinaopub.com2 ^) E) ?/ V! D/ e. N6 }
第l章风格, W" r; k. _0 R9 z
0 [2 A2 e- p& R& d% N& @0 C
语法驱动的编辑工具可以帮助避免这类错误4 F0 P3 U. {3 K1 P4 K. q4 R0 ^
虽然上面程序里的错误已经修正,但这个结果代码还是很难懂。如果我们用一个变量保
5 O* |, |) ?0 u% H. S存二月的天数,计算过程就很容易看明白了:. f% z# v/ M( y, X; e. S
f(month = FEB)i
! b7 r. Y  j+ Z! _. D6 nint nday/ ?( E8 Y$ m( V! u7 J3 H
nday = 28;- f  m+ x$ H5 {9 K
if (year%4== 0)- o9 i& r% e2 _6 [  s
nday =291 w8 Y; T& S+ f( U
if (day >nday)4 Y4 N* e" p9 B* ?7 ~
legal= False4 |1 d' d2 G8 M4 n+ P. G1 x# h
这段代码实际上还是错的—2000年是闰年,而1900和2100都不是。要把现在这个结构改正, ^/ R& R3 d; g8 |4 w0 C( Q
确是非常容易的。
) l  s. A$ _9 t" G7 I此外,如果你工作在一个不是自己写的程序上,请注意保留程序原有的风格。当你需要# i1 X, h; ^5 c% Y# @" d! v
做修改时,不要使用你自己的风格,即使你特别喜欢它。程序的一致性比你本人的习惯更重; l8 V7 ^: Z1 v9 ^0 `  F& j
要,因为这将使随你之后的其他人生活得更容易些。
6 t. h' P9 W% t  H; T1 s为了一致性,使用习惯用法。和自然语言一样,程序设计语言也有许多惯用法,也就是那些8 k: L! l  O1 r. A
经验丰富的程序员写常见代码片段的习惯方式。在学习一个语言的过程中,一个中心问题就
3 X% m# s6 n) h+ l是逐渐熟悉它的习惯用法。
# Z! U# b$ U3 y8 N3 c常见习惯用法之一是循环的形式。考虑在C、C++和Java中逐个处理n元数组中各个元素+ w  u+ b  m7 l  i1 Z
的代码,例如要对这些元素做初始化。有人可能写出下面的循环
9 O6 S) Y- H2 Dwhile (i =0;)) Y) H. H2 m3 W% ~. J0 o, @4 W7 X
array<i>= 1.0
( G) ]8 M7 x" Y6 E: F8 D! k1 m所有这些都正确,而习惯用法的形式却是
% _4 z9 [& A; S, w# Ofor (i =0: i
回复

使用道具 举报

该用户从未签到

5

主题

161

帖子

323

积分

普通会员

Rank: 2

积分
323
发表于 2022-7-19 23:07:47 | 显示全部楼层
6666666666666
回复 支持 反对

使用道具 举报

该用户从未签到

7

主题

151

帖子

289

积分

普通会员

Rank: 2

积分
289
发表于 2022-7-19 23:22:46 | 显示全部楼层
感谢分享!!
回复 支持 反对

使用道具 举报

该用户从未签到

4

主题

172

帖子

342

积分

普通会员

Rank: 2

积分
342
发表于 2022-7-19 23:32:52 | 显示全部楼层
666666666666666666666666666
回复 支持 反对

使用道具 举报

该用户从未签到

5

主题

160

帖子

317

积分

普通会员

Rank: 2

积分
317
发表于 2022-7-19 23:49:22 | 显示全部楼层
谢谢分享 学习下
回复 支持 反对

使用道具 举报

该用户从未签到

4

主题

150

帖子

296

积分

普通会员

Rank: 2

积分
296
发表于 2022-7-19 23:54:40 | 显示全部楼层
666, thanks
回复 支持 反对

使用道具 举报

该用户从未签到

0

主题

4493

帖子

8988

积分

普通会员

Rank: 2

积分
8988
发表于 2022-7-20 16:39:54 | 显示全部楼层
66666666666
回复 支持 反对

使用道具 举报

该用户从未签到

0

主题

4454

帖子

8912

积分

普通会员

Rank: 2

积分
8912
发表于 2022-8-11 13:28:32 | 显示全部楼层
可以下载
回复 支持 反对

使用道具 举报

该用户从未签到

0

主题

4405

帖子

8812

积分

普通会员

Rank: 2

积分
8812
发表于 2022-8-29 08:07:01 | 显示全部楼层
好好学习。。。666
回复 支持 反对

使用道具 举报

  • TA的每日心情
    开心
    2017-5-7 11:05
  • 签到天数: 1 天

    [LV.1]初学乍练

    0

    主题

    4467

    帖子

    8945

    积分

    普通会员

    Rank: 2

    积分
    8945
    发表于 2022-9-18 18:07:41 | 显示全部楼层
    非常好,顶一下
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|Archiver|手机版|小黑屋|Java自学网

    GMT+8, 2025-2-19 06:32 , Processed in 0.160763 second(s), 25 queries .

    Powered by Javazx

    Copyright © 2012-2022, Javazx Cloud.

    快速回复 返回顶部 返回列表