本书以C语言中的重难点“指针”为主题,提供了程序员所需的深入而完整的指针知识。作者结合多年的编程经验和感悟,从C指针的概念讲起,通过实验一步一步地揭示了指针和数组、内存、数据结构的关系,展现了指针的常见用法,揭示了各种使用技巧,还通过独特的方式告诉读者怎样解读C语言那些让人“纠结”的声明语法,以及如何绕过C指针的陷阱等。第□版基于64位操作系统对内容进行了大幅修订,新增了对ISO-C99、C11标准下相关内容的介绍等。
本书非常适合C语言中级学习者阅读,也可作为计算机专业学生学习C语言的参考。
前桥和弥(作者)
1969年出生,著有《自制编程语言》《彻底掌握C语言》《Java之谜和陷阱》等,其一针见血的“毒舌”文风和对编程语言深刻的见地受到广大读者的欢迎。
朱文佳(译者)
2005年毕业于上海大学计算机科学与技术学院,在对日通信行业从事开发及管理十余年,具有丰富的嵌入式C语言开发及项目管理经验。爱好阅读,自学日语,愿尽自己绵薄之力为大家分享优秀的外国图书。
第0章
本书目标与读者对象 1
0-1 本书目标 □
0-□ 读者对象与内容结构 5
□ □章
打好基础——预备知识和复习 9
1-1 C语言是什么样的语言 10
1-1-1 C语言的发展历程 10
是汇编语言还是汇编器 11
B语言是什么样的语言 1□
1-1-□ 不完备和不统一的语法 13
1-1-3 C语言“圣经”——K&R 13
1-1-4 ANSI C之前的C语言 14
1-1-5 ANSI C(C89/90) 16
1-1-6 C95 16
1-1-7 C99 17
1-1-8 C11 18
1-1-9 C语言的理念 19
1-1-10 C语言的主体 □0
1-1-11 C语言曾是只能使用标量的语言 □1
1-□ 内存和地址 □3
1-□-1 内存和地址 □3
1-□-□ 内存和变量 □5
size_t类型 □7
1-□-3 内存和程序运行 □7
1-3 关于指针 □9
1-3-1 恶名昭著的指针究竟是什么 □9
1-3-□ 和指针的□ □次亲密接触 30
1-3-3 地址运算符、间接运算符、下标运算符 34
关于本书中的地址值——16进制表示法 35
混乱的声明——如何自然地理解声明 35
杂谈:hoge是什么 37
1-3-4 指针和地址之间的微妙关系 38
在运行时既没有类型信息,也没有变量名 40
1-3-5 指针运算 41
1-3-6 何谓空指针 4□
NULL和0和'\0' 43
1-3-7 实践——从函数返回多个值 46
形参与实参 50
1-4 关于数组 51
1-4-1 使用数组 51
C语言的数组是从0开始的 53
1-4-□ 数组与指针的微妙关系 54
1-4-3 下标运算符[]与数组毫无关系 56
语法糖 59
1-4-4 为何存在指针运算这种奇怪功能 59
1-4-5 别再滥用指针运算了 61
更改参数的做法可取吗 6□
1-4-6 试图将数组作为函数参数传递 63
如果对数组进行值传递 65
1-4-7 声明函数形参的方法 66
C语言为什么不进行数组边界检查 67
1-4-8 C99中的可变长数组 68
第 □章
做个实验——C语言是怎样使用内存的 71
□-1 虚拟地址 7□
关于scanf() 75
未定义、未指定、实现定义 77
□-□ C语言中内存的使用方法 78
□-□-1 C语言中变量的种类 78
□-□-□ 尝试输出地址 80
存储类说明符 80
□-3 函数与字符串字面量 85
□-3-1 只读内存区域 85
□-3-□ 指向函数的指针 86
□-4 静态变量 88
□-4-1 什么是静态变量 88
□-4-□ 分割编译与链接 88
□-5 自动变量(栈) 91
□-5-1 内存空间的“重复使用” 91
□-5-□ 函数调用究竟发生了什么 91
调用约定 95
□-5-3 自动变量的引用 95
一旦函数执行结束,自动变量的内存空间就会被释放 98
□-5-4 典型的安全漏洞——缓冲区溢出漏洞 99
操作系统针对缓冲区溢出漏洞给出的对策 10□
□-5-5 可变长参数 103
assert() 106
试写一个用于调试的函数 107
□-5-6 递归调用 110
□-5-7 C99中的可变长数组(VLA)的栈 113
□-6 利用malloc()动态分配内存(堆) 116
□-6-1 malloc()的基础知识 116
应该强制转换malloc()的返回值类型吗 119
□-6-□ malloc()是系统调用吗 119
□-6-3 malloc()中发生了什么 1□0
□-6-4 free()之后相应的内存空间会怎样 1□□
Valgrind 1□4
□-6-5 碎片化 1□4
□-6-6 malloc()以外的动态内存分配函数 1□5
假如malloc()参数为0 1□7
malloc()的返回值检查 1□8
程序结束时也必须调用free()吗 1□9
□-7 对齐 131
结构体的成员名称在运行时也是缺失的 134
□-8 字节序 135
□-9 关于语言规范和实现——抱歉,前面的内容都是骗你的 137
第3章
语法揭秘——它到底是怎么回事 139
3-1 解读C语言声明 140
3-1-1 用英语阅读 140
3-1-□ 解读C语言声明 141
近来的语言多数是将类型后置的 144
3-1-3 类型名 145
如果把间接运算符*后置 146
3-□ C语言数据类型的模型 147
3-□-1 基本类型和派生类型 147
3-□-□ 指针类型的派生 148
3-□-3 数组类型的派生 150
3-□-4 什么是指向数组的指针 150
3-□-5 C语言中不存在多维数组 15□
3-□-6 函数类型的派生 154
3-□-7 计算类型的长度 155
3-□-8 基本类型 157
3-□-9 结构体和联合体 159
3-□-10 不完全类型 159
3-3 表达式 16□
3-3-1 表达式和数据类型 16□
对“表达式”使用sizeof 164
3-3-□ 什么是左值——变量的两张面孔 166
“左值”的由来 167
3-3-3 数组→指针的转换 168
3-3-4 与数组和指针相关的运算符 169
3-3-5 多维数组 171
运算符的优先级 173
3-4 解读C语言声明(续) 176
3-4-1 const修饰符 176
3-4-□ 如何使用const?可以用到哪种程度 178
const可以代替#define吗 181
3-4-3 typedef 181
3-5 其他 185
3-5-1 函数形参的声明(ANSI C版) 185
K&R中关于函数形参声明的说明 186
3-5-□ 函数形参的声明(C99版) 188
3-5-3 关于空的下标运算符[] 189
定义与声明 191
3-5-4 字符串字面量 19□
字符串字面量是char的数组 194
3-5-5 关于指向函数的指针引发的混乱 195
3-5-6 强制类型转换 196
3-5-7 练习——解读复杂声明 198
3-6 请记住:数组与指针截然不同 □03
3-6-1 你为什么感到混乱 □03
3-6-□ 在表达式中 □04
3-6-3 在声明中 □06
第4章
数组和指针的常见用法 □09
4-1 基本用法 □10
4-1-1 通过返回值以外的方法返回 □10
4-1-□ 将数组作为函数的参数传递 □11
4-1-3 动态数组——通过malloc()分配的可变长数组 □1□
其他语言的数组 □14
4-□ 组合使用 □16
4-□-1 动态数组的数组 □16
宽字符 □□3
4-□-□ 动态数组的动态数组 □□5
4-□-3 命令行参数 □□8
4-□-4 通过参数返回指针 □30
什么是“双指针” □35
4-□-5 将多维数组作为函数的参数传递 □36
4-□-6 将多维数组作为函数的参数传递(VLA版) □37
4-□-7 通过malloc()分配纵横可变的二维数组(C99) □39
C语言中的多维数组是行优先的 □40
纵横可变的二维数组的ANSI C实现 □41
Java和C#的多维数组 □4□
4-□-8 数组的动态数组 □43
4-□-9 在考虑可变之前,不妨考虑使用结构体 □44
4-□-10 可变长结构体(ANSI C版) □46
关于分配可变长结构体时的长度指定 □48
4-□-11 柔性数组成员(C99) □48
指针可以指向数组的□后一个元素的下一个元素 □49
第5章
数据结构——指针的真正用法 □51
5-1 案例学习1:计算单词的使用频率 □5□
5-1-1 案例的需求 □5□
各种语言中指针的叫法 □53
引用传递 □53
5-1-□ 设计 □56
关于头文件的写法 □59
5-1-3 数组版 □61
5-1-4 链表版 □65
头文件的公有和私有 □71
当需要同时处理多个数据时 □7□
迭代器 □73
5-1-5 添加查找功能 □75
翻倍游戏 □77
5-1-6 其他数据结构 □77
5-□ 案例学习□:绘图工具的数据结构 □83
5-□-1 案例的需求 □83
5-□-□ 表示各种图形 □84
关于坐标系 □85
5-□-3 Shape类型 □86
5-□-4 讨论——还有其他方法吗 □89
能保存任何类型的链表 □93
5-□-5 图形的组合 □94
5-□-6 通过指向函数的指针的数组分配处理 300
5-□-7 通往继承与多态之路 30□
将draw()放入Shape中真的好吗 30□
5-□-8 指针的可怕之处 304
5-□-9 那么,指针到底是什么呢 305
第6章
其他——拾遗 307
6-1 新的函数组 308
6-1-1 添加了范围检查的函数(C11) 308
restrict关键字 310
6-1-□ 无须使用静态存储空间的函数(C11) 311
6-□ 陷阱 314
6-□-1 整数提升 314
6-□-□ 如果在(老式的)C语言中使用float类型的参数 316
6-□-3 printf()与scanf() 318
6-□-4 原型声明的光与影 319
6-3 惯用写法 3□1
6-3-1 结构体声明 3□1
6-3-□ 自引用结构体 3□□
6-3-3 结构体的相互引用 3□3
6-3-4 结构体的嵌套 3□4
6-3-5 联合体 3□5
6-3-6 无名结构体和无名联合体(C11) 3□6
6-3-7 数组的初始化 3□7
6-3-8 指向char的指针的数组的初始化 3□8
6-3-9 结构体的初始化 3□9
6-3-10 联合体的初始化 330
6-3-11 指定初始化(C99) 331
6-3-1□ 复合字面量(C99) 33□
参考文献 334