第1篇 代码表面 3
第1章 善于防守:健壮代码的防御性编程技巧 3
1.1向优秀的代码前进 3
1.2设想:最坏的选择 4
1.3什么是防御性编程? 6
1.4又大又坏的世界 8
1.5防御性编程技巧 9
1.5.1使用好的编码风格和合理的设计 9
1.5.2不要仓促地编写代码 9
1.5.3不要相信任何人 10
1.5.4编码的目标是清晰,而不是简洁 11
1.5.5不要让任何人做他们不该做的修补工作 11
1.5.6编译时打开所有警告开关 12
1.5.7使用静态分析工具 13
1.5.8使用安全的数据结构 13
1.5.9检查所有的返回值 14
1.5.10审慎地处理内存(和其他宝贵的资源) 14
1.5.11在声明位置初始化所有变量 15
1.5.12尽可能推迟一些声明变量 15
1.5.13使用标准语言工具 15
1.5.14使用好的诊断信息日志工具 16
1.5.15审慎地进行强制转换 16
1.5.16细则 16
1.6约束 17
1.6.1约束的内容 19
1.6.2移除约束 19
1.7总结 22
1.8另请参见 22
1.9思考 24
1.9.1深入思考 24
1.9.2结合自己 24
第2章 精心布局:源代码的版面和样式 26
2.1什么是关键 27
2.2了解你的读者 27
2.3什么是好的样式 29
2.4使用括号 30
2.4.1 K&R括号风格 30
2.4.2悬挂式的括号风格 31
2.4.3缩进的括号风格 32
2.4.4其他的括号风格 33
2.5主宰一切的风格 33
2.6内部风格(以及在哪里使用它们) 35
2.7设立标准 37
2.8正义的战争 39
2.9总结 40
2.10另请参见 42
2.11思考 42
2.11.1深入思考 42
2.11.2结合自己 43
第3章 名正言顺:为有意义的事物起有意义的名称 45
3.1为什么我们应该恰当地命名呢 47
3.2我们对什么进行命名 47
3.3名字游戏 48
3.3.1描述性 48
3.3.2技术上正确 48
3.3.3符合语言习惯 49
3.3.4恰当 49
3.4具体细节 50
3.4.1命名变量 51
3.4.2命名函数 52
3.4.3命名类型 53
3.4.4命名名字空间 54
3.4.5命名宏 55
3.4.6命名文件 56
3.5玫瑰不叫玫瑰 57
3.5.1保持前后一致 58
3.5.2利用上下文 58
3.5.3使用对你有利的名称 59
3.6总结 59
3.7另请参见 61
3.8思考 62
3.8.1深入思考 62
3.8.2结合自己 63
第4章 不言自明:编写“自文档化”代码的技巧 64
4.1自文档化的代码 66
4.2编写自文档化代码的技术 69
4.2.1使用好的样式编写简单的代码 69
4.2.2选择有意义的名称 70
4.2.3分解为原子函数 70
4.2.4选择描述性的类型 71
4.2.5命名常量 71
4.2.6强调重要的代码 72
4.2.7分组相关信息 72
4.2.8提供文件头 72
4.2.9恰当地处理错误 73
4.2.10编写有意义的注释 73
4.3实用的自文档化方法 74
4.3.1文学性编程 74
4.3.2文档化工具 76
4.4总结 78
4.5另请参见 79
4.6思考 79
4.6.1深入思考 79
4.6.2结合自己 81
第5章 随篇注释:如何编写代码注释 82
5.1什么是代码注释 83
5.2注释看上去是什么样的 84
5.3多少注释是恰当的 84
5.4注释中应该有些什么 85
5.4.1解释为什么,而不是怎么样 85
5.4.2不要描述代码 86
5.4.3不要取代代码 86
5.4.4确保注释有用 86
5.4.5避免分心 88
5.5实践 88
5.6从审美的角度看注释 89
5.6.1一致性 89
5.6.2清晰的块注释 90
5.6.3缩进的注释 90
5.6.4行尾注释 91
5.6.5帮助你阅读代码 91
5.6.6选择一种维护成本较低的风格 92
5.6.7分隔板 92
5.6.8标志 92
5.6.9文件头注释 93
5.7使用注释 94
5.7.1帮助你编写例行程序 94
5.7.2错误修正通告 95
5.7.3注释过时 95
5.7.4维护和空洞无物的注释 96
5.8总结 97
5.9另请参见 98
5.10思考 98
5.10.1深入思考 98
5.10.2结合自己 99
第6章 人非圣贤:处理不可避免的情况——代码中的错误情形 100
6.1从何而来 101
6.2错误报告机制 102
6.2.1不报告 103
6.2.2返回值 103
6.2.3错误状态变量 104
6.2.4异常 104
6.2.5信号 106
6.3检测错误 107
6.4处理错误 108
6.4.1何时处理错误 109
6.4.2可能的反应 110
6.4.3代码示例 112
6.5使地狱浮现 116
6.6管理错误 118
6.7总结 119
6.8另请参见 119
6.9思考 120
6.9.1深入思考 120
6.9.2结合自己 121
第2篇 代码的神秘生命 125
第7章 欲善其事,先利其器:使用工具构建软件 125
7.1什么是软件工具 126
7.2为什么要在意工具 128
7.3使工具发挥作用 129
7.3.1了解它能做些什么 130
7.3.2学习如何驾驭它 130
7.3.3了解它适合什么任务 131
7.3.4检查它是否可用 131
7.3.5找到了解更多信息的途径 131
7.3.6查明新版本何时出现 132
7.4哪个工具 132
7.4.1源代码编辑工具 133
7.4.2代码构建工具 136
7.4.3调试和调查工具 138
7.4.4语言支持工具 140
7.4.5其他工具 141
7.5总结 142
7.6另请参见 143
7.7思考 144
7.7.1深入思考 144
7.7.2结合自己 145
第8章 测试时代:测试代码的魔术 146
8.1反思现实 148
8.2谁、是什么、何时以及为什么 149
8.2.1我们为什么要测试 149
8.2.2谁来进行测试 150
8.2.3测试的内容有些什么 150
8.2.4何时进行测试 151
8.3测试并不难 152
8.4测试的类型 156
8.5选择单元测试用例 160
8.6为测试而设计 163
8.7看!不要用手 164
8.8面对故障该怎么办 165
8.9你能管理它吗 166
8.9.1缺陷跟踪系统 166
8.9.2 bug审查 168
8.10总结 169
8.11另请参见 169
8.12思考 170
8.12.1深入思考 170
8.12.2结合自己 171
第9章 寻找缺陷(调试):当事情进展得不顺利时该怎么办 172
9.1生活的真相 173
9.2 bug的种类 174
9.2.1从远处看 174
9.2.2从近处看 175
9.2.3从更近处看 178
9.3消灭害虫 180
9.3.1地下之路 181
9.3.2地上之路 181
9.4搜寻bug 182
9.4.1编译时错误 182
9.4.2运行时错误 184
9.5如何修正缺陷 188
9.6预防 190
9.7除蜂剂、驱虫剂、捕蝇纸 190
9.7.1调试器 190
9.7.2内存访问校验器 191
9.7.3系统调用跟踪 191
9.7.4内核转储 191
9.7.5日志 191
9.8总结 192
9.9另请参见 193
9.10思考 194
9.10.1深入思考 194
9.10.2结合自己 194
第10章 代码构建:将源代码转换为可执行代码的过程 196
10.1语言障碍 197
10.1.1解释型语言 198
10.1.2编译型语言 199
10.1.3字节编译型语言 200
10.2小题大做 201
10.3构建软件版本 203
10.4怎样才算是一个优秀的构建系统 206
10.4.1简洁 206
10.4.2一致 207
10.4.3可重复和可靠 207
10.4.4原子性 208
10.4.5能够应付错误 209
10.5技术细节 210
10.5.1目标的选择 210
10.5.2内务处理 212
10.5.3依赖关系 212
10.5.4自动构建 213
10.5.5构建配置 214
10.5.6递归地使用make 215
10.6请发布我吧 215
10.7构建大师是全能的吗 218
10.8总结 218
10.9另请参见 219
10.10思考 219
10.10.1深入思考 220
10.10.2结合自己 220
第11章 追求速度:优化程序和编写高效的代码 222
11.1优化是什么 223
11.2是什么使代码不尽如人意 224
11.3为什么不进行优化呢 225
11.4为什么要进行优化 228
11.5优化的具体细节 229
11.5.1证明你需要进行优化 230
11.5.2找出运行得最慢的代码 230
11.5.3测试代码 232
11.5.4优化代码 233
11.5.5优化之后 233
11.6优化的技术 233
11.6.1设计更改 234
11.6.2代码更改 237
11.7编写高效的代码 241
11.8总结 243
11.9另请参见 244
11.10思考 244
11.10.1深入思考 244
11.10.2结合自己 245
第12章 不安全感综合征:编写安全的程序 247
12.1危险 248
12.2敌人 250
12.3借口,都是借口 252
12.4感到很脆弱 253
12.4.1不安全的设计和体系结构 253
12.4.2缓冲溢出 254
12.4.3嵌入的查询字符串 255
12.4.4竞争状况 255
12.4.5整数溢出 256
12.5防范措施 257
12.5.1系统安装技术 258
12.5.2软件设计技术 258
12.5.3代码实现技术 260
12.5.4规程技术 261
12.6总结 261
12.7另请参见 262
12.8思考 263
12.8.1深入思考 263
12.8.2结合自己 263
第3篇 代码的形成过程 267
第13章 崇尚设计:如何创作出优秀的软件设计 267
13.1边设计边编程 268
13.2我们要设计什么 269
13.3为什么这么忙乱 270
13.4良好的软件设计 271
13.4.1简洁 272
13.4.2优雅 273
13.4.3模块化 274
13.4.4良好的接口 275
13.4.5可扩展性 278
13.4.6避免重复 278
13.4.7可移植性 279
13.4.8符合语言习惯 280
13.4.9良好地文档化 280
13.5如何设计代码 280
13.5.1设计方法和过程 281
13.5.2设计工具 282
13.6总结 285
13.7另请参见 285
13.8思考 286
13.8.1深入思考 286
13.8.2结合自己 287
第14章 软件体系结构:奠定软件设计的基础 288
14.1什么是软件体系结构 289
14.1.1软件蓝图 289
14.1.2视图 290
14.1.3在何时和何处进行体系结构设计 292
14.1.4用体系结构来做什么 293
14.1.5关于组件和连接 294
14.2什么是良好的体系结构 295
14.3体系结构风格 297
14.3.1没有体系结构 297
14.3.2分层的体系结构 298
14.3.3管道和过滤器体系结构 299
14.3.4客户端/服务器体系结构 299
14.3.5基于组件的体系结构 302
14.3.6框架 303
14.4总结 303
14.5另请参见 304
14.6思考 305
14.6.1深入思考 305
14.6.2结合自己 305
第15章 改良与革命:代码是如何成长的 307
15.1软件腐烂 308
15.2警告信号 310
15.3代码是如何成长的 312
15.4相信不可能之事 315
15.5对此我们可以做些什么? 316
15.5.1编写新代码 316
15.5.2维护现有代码 317
15.6总结 319
15.7另请参见 319
15.8思考 320
15.8.1深入思考 321
15.8.2结合自己 321
第4篇 “一群”程序员 325
第16章 代码猴子:培养正确的编程态度和方法 325
16.1各种各样的猴子 326
16.1.1卖力工作的程序员 327
16.1.2代码猴子 328
16.1.3权威 329
16.1.4半权威 330
16.1.5傲慢的天才 331
16.1.6牛仔 333
16.1.7规划者 334
16.1.8老前辈 335
16.1.9狂热者 336
16.1.10单线条程序员 337
16.1.11拖沓者 338
16.1.12勉强的团队领导 339
16.1.13你 340
16.2理想的程序员 340
16.3那么该怎么办 341
16.4最愚蠢的人 342
16.5总结 343
16.6另请参见 343
16.7行为表格 344
16.8思考 345
16.8.1深入思考 345
16.8.2结合自己 345
第17章 团结就是力量:团队合作与个人程序员 347
17.1我们的团队——概览 348
17.2团队组织 350
17.2.1管理方法 350
17.2.2责任划分 350
17.2.3组织和代码结构 352
17.3团队合作工具 352
17.4团队疾病 354
17.4.1巴别塔 354
17.4.2独裁制 356
17.4.3民主制 357
17.4.4卫星站 359
17.4.5大峡谷 361
17.4.6流沙 363
17.4.7旅鼠 365
17.5良好团队合作的个人技巧和特点 366
17.5.1沟通 366
17.5.2谦虚 367
17.5.3处理冲突 367
17.5.4学习和适应能力 369
17.5.5了解你的不足之处 369
17.6团队合作原则 370
17.6.1集体代码所有制 370
17.6.2尊重别人的代码 371
17.6.3编码准则 371
17.6.4定义成功 371
17.6.5定义责任 372
17.6.6避免倦怠 372
17.7团队的生命周期 372
17.7.1团队的创建 373
17.7.2团队的成长 375
17.7.3团队合作 377
17.7.4团队结束 377
17.8总结 380
17.9另请参见 381
17.10行为表格 382
17.11思考 383
17.11.1深入思考 383
17.11.2结合自己 383
第18章 安全措施:源代码控制与自我控制 385
18.1我们的责任 386
18.2源代码控制 387
18.2.1修订控制 388
18.2.2访问控制 390
18.2.3处理代码库 390
18.2.4在代码树上创建分支 391
18.2.5源代码控制简史 393
18.3配置管理 393
18.4备份 395
18.5发布源代码 396
18.6应该将源代码放在哪里 397
18.7总结 398
18.8另请参见 399
18.9思考 400
18.9.1深入思考 400
18.9.2结合自己 400
第5篇 开发过程的组成部分 403
第19章 注意细节:编写软件规范 403
19.1规范到底是什么 404
19.2规范的类型 405
19.2.1需求规范 407
19.2.2功能规范 409
19.2.3系统体系结构规范 410
19.2.4用户界面规范 410
19.2.5设计规范 411
19.2.6测试规范 412
19.3规范应当包含哪些内容 413
19.4规范编写过程 415
19.5我们为什么会不编写规范 418
19.6总结 420
19.7另请参见 420
19.8思考 421
19.8.1深入思考 421
19.8.2结合自己 421
第20章 代码审查:执行代码审查 423
20.1什么是代码审查 424
20.2何时进行审查 425
20.2.1是否要进行审查 426
20.2.2审查哪些代码 427
20.3执行代码审查 427
20.3.1代码审查会议 428
20.3.2集成审查 431
20.4审查你的态度 432
20.4.1作者的态度 432
20.4.2审查人员的态度 433
20.5完美的代码 434
20.6代码审查之外 435
20.7总结 436
20.8另请参见 436
20.9清单 437
20.10思考 438
20.10.1深入思考 438
20.10.2结合自己 438
第21章 时间估计:软件时间范围估计的魔术 439
21.1在黑暗中摸索 440
21.2为什么估计这么困难 441
21.3压力之下 443
21.4实用的估计方法 444
21.5计划游戏 447
21.6坚持 451
21.7总结 454
21.8另请参见 454
21.9思考 455
21.9.1深入思考 455
21.9.2结合自己 455
第6篇 从高处鸟瞰 459
第22章 程序秘方:代码开发的方法和过程 459
22.1编程风格 460
22.1.1结构化编程 461
22.1.2面向对象的程序设计 462
22.1.3函数式编程 463
22.1.4逻辑编程 464
22.2烹饪方法:做什么与怎样做 464
22.3开发过程 465
22.3.1混乱 466
22.3.2瀑布模型 468
22.2.3 SSADM和PRINCE 470
22.3.4 V模型 470
22.3.5原型设计 471
22.3.6迭代和增量开发 472
22.3.7螺旋模型 473
22.3.8敏捷的方法 474
22.3.9其他开发过程 475
22.4已经够了 476
22.5选择一种过程 477
22.6总结 478
22.7另请参见 478
22.8思考 479
22.8.1深入思考 479
22.8.2结合自己 479
第23章 编程领域大观:不同的编程分支 481
23.1应用程序编程 482
23.1.1塑装软件 483
23.1.2定制应用程序 484
23.2游戏编程 485
23.3系统编程 486
23.4嵌入式编程 488
23.5分布式编程 490
23.6网络应用程序编程 492
23.7企业编程 494
23.8数字编程 495
23.9那又怎样 497
23.10总结 497
23.11另请参见 498
23.12思考 498
23.12.1深入思考 499
23.12.2结合自己 499
第24章 下一步呢:结果好就一切都好 500
但下一步该做什么呢? 501
答案和讨论 504
参考书目 607