第一部分 计算机存储:内存和文件 2
第1章 程序的执行 2
1.1编译 2
1.2重定向输出 6
第2章 栈内存 7
2.1值和地址 7
2.2栈 8
2.3调用栈 9
2.3.1返回位置 9
2.3.2函数实参 12
2.3.3局部变量 14
2.3.4值地址 15
2.3.5数组 16
2.3.6获取地址 17
2.4可见度 17
2.5习题 20
2.5.1绘制调用栈Ⅰ 20
2.5.2绘制调用栈Ⅱ 20
2.5.3地址 21
2.6习题解答 21
2.6.1绘制调用栈Ⅰ 21
2.6.2绘制调用栈Ⅱ 22
2.6.3地址 22
2.7在DDD(命令行调试程序)上检测调用栈 22
第3章 预防、检测及消除bug 26
3.1开发软件≠编码 26
3.1.1编程前 26
3.1.2编程中 27
3.1.3编程后 28
3.2常见错误 28
3.2.1未初始化变量 28
3.2.2错误数组下标 28
3.2.3错误数据类型 28
3.3后执行式和交互式调试 28
3.4生产代码与测试代码分离 29
第4章 指针 30
4.1作用域 30
4.2 swap函数 31
4.3指针 33
4.4再论swap函数 37
4.5类型错误 39
4.6数组和指针 40
4.7类型规则 43
4.8指针运算 44
4.9习题 47
4.9.1 swap函数1 47
4.9.2 swap函数2 48
4.9.3 swap函数3 48
4.9.4 swap函数4 48
4.9.5 swap函数5 49
4.9.6 15552种变化 49
4.10习题解答 50
4.10.1 swap函数1 50
4.10.2 swap函数2 50
4.10.3 swap函数3 51
4.10.4 swap函数4 51
4.10.5 swap函数5 51
第5章 编写和测试程序 52
5.1不同的数组元素 52
5.1.1 main函数 52
5.1.2 areDistinct函数 53
5.1.3编译和链接 54
5.1.4 make工具 55
5.2使用Makefile测试 57
5.2.1生成测试用例 58
5.2.2重定向输出 58
5.2.3使用diff去比较输出 58
5.2.4添加测试到Makefile 59
5.3无效的内存访问 60
5.4使用valgrind检查内存访问错误 62
5.5测试覆盖 64
5.6限制内核大小 67
5.7带有死循环的程序 67
第6章 字符串 69
6.1字符数组 69
6.2 C语言中的字符串函数 72
6.2.1复制函数:strcpy 72
6.2.2比较函数:strcmp 73
6.2.3寻找子字符串函数:strstr 73
6.2.4寻找字符函数:strchr 74
6.3理解argv 74
6.4对子字符串计数 77
第7章 编程问题和调试 80
7.1实现字符串函数 80
7.1.1 C语言库 80
7.1.2头文件 80
7.1.3 mystring.h 82
7.1.4创建输入和正确输出 82
7.1.5 Makefile 86
7.1.6 mystring.c 86
7.1.7使用const 88
7.2调试 89
7.2.1找到死循环 90
7.2.2找到无效内存访问 91
7.2.3检测无效内存访问 92
第8章 堆内存 94
8.1用malloc函数创建数组 94
8.2栈和堆 96
8.3返回堆地址的函数 98
8.4 C语言中的二维数组 99
8.5指针和参数 101
第9章 使用堆内存的编程问题 104
9.1对数组排序 104
9.1.1生成测试输入和期望输出 104
9.1.2重定向输入 105
9.1.3整数排序 107
9.1.4使用valgrind检测内存泄漏 110
9.2使用qsort进行排序 111
9.2.1 qsort 111
9.2.2比较函数 112
9.2.3执行范例 114
9.2.4对字符串排序 115
第10章 读写文件 118
10.1通过argv传递一个文件名 118
10.2读取文件 119
10.2.1读取字符型:fgetc 119
10.2.2读取整型:fscanf(...1389896...) 121
10.3写入文件 123
10.4读写字符串 125
第11章 编程解决使用文件的问题 128
11.1对文件中的整数进行排序 128
11.2计算字符出现的次数 130
11.3计算单词出现的次数 132
11.4如何注释程序 134
第二部分 递归 138
第12章 递归 138
12.1在限制条件下选取小球 138
12.1.1双色球问题 138
12.1.2三色球问题 139
12.1.3附加限制条件 140
12.2单行道 142
12.3汉诺塔 143
12.4计算整数分拆 145
12.4.1计算“1”的个数 147
12.4.2仅使用奇数进行分拆 148
12.4.3使用递增数进行分拆 148
12.4.4交替使用奇偶数进行分拆 149
12.4.5整数分拆问题的推广 151
12.4.6解决分拆问题的错误方法 151
第13章 递归函数 152
13.1在限制条件下选取小球 152
13.2单行道 155
13.3汉诺塔 156
13.4整数分拆 158
13.5阶乘 159
13.6斐波那契数列 161
13.7利用gprof进行性能分析 165
第14章 整数分拆 167
14.1堆内存和栈内存 168
14.2追踪递归函数调用 176
14.3约束条件下的分拆 178
14.3.1仅使用奇数进行分拆 179
14.3.2使用递增数进行分拆 179
14.3.3交替使用奇偶数进行分拆 180
14.3.4使用gprof和gcov查找性能瓶颈 180
第15章 使用递归解决问题 187
15.1二分搜索 187
15.2快速排序 189
15.3排列组合 195
15.4栈排序 198
15.4.1例子1 199
15.4.2例子2 199
15.4.3例子3 199
15.4.4例子4 199
15.4.5可排序栈 200
15.5追踪递归函数 203
15.6一个存在错误的递归函数 205
第三部分 结构 208
第16章 程序员可定义数据类型 208
16.1结构体和对象 208
16.2作为实参传递对象 212
16.3对象和指针 214
16.3.1返回一个对象 216
16.3.2对象和malloc 216
16.4构造函数和析构函数 218
16.5结构中的结构 224
16.6二进制文件和对象 226
第17章 使用结构的编程问题 230
17.1个人信息库排序 230
17.2压缩十进制数位 235
17.2.1数制 235
17.2.2用1字节表达2个十进制数位 236
17.2.3位运算 236
17.2.4压缩和恢复十进制 239
17.2.5十进制压缩编程 239
17.3二进制文件和指针 243
第18章 链表 245
18.1可扩展类型 245
18.2链表 246
18.3链表的插入 246
18.4链表的查找 248
18.5从链表中删除 249
18.6打印链表 252
18.7链表的销毁 253
第19章 使用链表的编程问题 256
19.1队列 256
19.2数字排序 256
19.3稀疏数组 257
19.4单链表反转 262
第20章 二叉搜索树 264
20.1二叉搜索树 265
20.2二叉搜索树的插入 266
20.3二叉搜索树的搜索 269
20.4二叉搜索树的遍历 269
20.5二叉搜索树的删除 272
20.6二叉搜索树的销毁 274
20.7主函数main 274
20.8链接器Makefike 275
20.9不同的二叉树结构 275
第21章 线程并行编程 278
21.1并行编程 278
21.2多任务处理 278
21.3 POSIX线程 279
21.4子集和 280
21.4.1生成测试实例 281
21.4.2字典顺序处理 283
21.4.3多线程处理 287
21.5多线程处理过程的交叉运行 289
21.6线程同步 293
21.7阿姆达尔定律 295
第四部分 应用 298
第22章 寻找迷宫出口 298
22.1迷宫的文件格式 298
22.2读取迷宫文件 299
22.3迷宫结构体 303
22.4逃跑策略 306
22.5策略的实现 308
22.5.1 canMove函数 308
22.5.2 getout函数 309
22.5.3打印访问过的位置 313
第23章 图像处理 316
23.1图像结构体 316
23.2图像处理 321
23.2.1图像像素和颜色 321
23.2.2处理函数 322
23.2.3应用一个颜色滤波器 322
23.2.4图像颜色反转 324
23.2.5边缘检测 324
23.2.6颜色均衡 326
第24章 霍夫曼压缩 329
24.1例程 329
24.2编码 330
24.2.1计算频率 330
24.2.2按频率排序 332
24.2.3构建编码树 334
24.2.4创建编码本 342
24.2.5压缩文件 346
24.2.6位压缩 349
24.3解码 353
附录A Linux 370
附录B版本控制 373
附录C集成开发环境 376
索引 385