Linux C编程一站式学习

宋劲杉

北京亚嵌教育研究中心

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with the Invariant Sections being 前言, with no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in 附录 C, GNU Free Documentation License Version 1.3, 3 November 2008.

2009.2.27

修订历史
修订 0.62009.2.27

添加了GFDL许可证,正式网络发布。第三部分还很粗糙,错误也有不少,有待改进。第一部分和第二部分已经比较成熟,第二部分还差三章没写。


目录

历史
前言
I. C语言入门
1. 程序的基本概念
程序和编程语言
自然语言和形式语言
程序的调试
第一个程序
2. 常量、变量和表达式
继续Hello World
常量
变量
赋值
表达式
字符类型与字符编码
3. 简单函数
数学函数
自定义函数
形参和实参
局部变量与全局变量
4. 分支语句
if语句
if/else语句
布尔代数
switch语句
5. 深入理解函数
return语句
增量式开发
递归
6. 循环语句
while语句
do/while语句
for语句
break和continue语句
嵌套循环
goto语句
7. 结构体
复合数据类型--结构体
数据抽象
数据类型标志
嵌套结构体
8. 数组
数组的基本操作
数组应用实例:统计随机数
数组应用实例:直方图
字符串
多维数组
9. 编码风格
缩进和空白
注释
标识符命名
函数
indent工具
10. gdb
单步执行和跟踪函数调用
断点
观察点
段错误
11. 排序与查找
算法的概念
插入排序
算法的时间复杂度分析
归并排序
线性查找
折半查找
12. 栈与队列
数据结构的概念
堆栈
深度优先搜索
队列与广度优先搜索
环形队列
13. 本阶段总结
C语言基本语法
思维方法与编程思想
调试方法
II. C语言本质
14. 计算机中数的表示
为什么计算机用二进制计数
不同进制之间的换算
整数的加减运算
浮点数
15. 数据类型详解
整型
浮点型
类型转换
Integer Promotion
Usual Arithmetic Conversion
由赋值产生的类型转换
强制类型转换
编译器如何处理类型转换
16. 运算符详解
位运算
按位与、或、异或、取反运算
移位运算
掩码
异或运算的一些特性
其它运算符
复合赋值运算符
条件运算符
逗号运算符
sizeof运算符与typedef类型声明
Side Effect与Sequence Point
运算符总结
17. 计算机体系结构基础
内存与地址
CPU
设备
MMU
Memory Hierarchy
18. x86汇编程序基础
最简单的汇编程序
x86的寄存器
第二个汇编程序
寻址方式
ELF文件
目标文件
可执行文件
19. 汇编与C之间的关系
函数调用
main函数和启动例程
变量的存储布局
结构体和联合体
C内联汇编
20. 链接详解
多目标文件的链接
定义和声明
externstatic关键字
头文件
定义和声明的详细规则
静态库
共享库
编译、链接、运行
动态链接的过程
共享库的命名惯例
虚拟内存管理
21. 预处理
预处理的步骤
宏定义
函数式宏定义
内联函数
###运算符和可变参数
宏展开的步骤
条件预处理指示
其它预处理特性
22. Makefile基础
基本规则
隐含规则和模式规则
变量
自动处理头文件的依赖关系
常用的make命令行选项
23. 指针
指针的基本操作
指针类型的参数和返回值
指针与数组
指针与const限定符
指针与结构体
指向指针的指针与指针数组
指向数组的指针与多维数组
函数类型和函数指针类型
不完全类型和复杂声明
24. 函数接口
本章的预备知识
strcpystrncpy
mallocfree
传入参数与传出参数
两层指针的参数
返回值是指针的情况
回调函数
可变参数
25. C标准库
字符串操作函数
初始化字符串
取字符串的长度
拷贝字符串
连接字符串
比较字符串
搜索字符串
分割字符串
标准I/O库函数
文件的基本概念
fopen/fclose
stdin/stdout/stderr
errno与perror函数
以字符为单位的读写函数fgetc/fputc
文件读写位置的定位函数
以字符串为单位的读写函数fgets/fputs
带缓存I/O
本节编码规范总结
数值转换函数
本章综合练习
26. 链表
编写中
27. 二叉树和哈希表
编写中
III. Linux系统编程
28. 文件与I/O
汇编程序的Hello world
C标准I/O库函数与Unbuffered I/O函数
open/close
read/write
lseek
fcntl
ioctl
mmap
29. 文件系统
引言
ext2文件系统
总体存储布局
实例剖析
数据块寻址
文件和目录操作的系统函数
VFS
内核数据结构
dup和dup2函数
30. 进程
引言
环境变量
进程控制
fork函数
exec函数
wait和waitpid函数
进程间通信
管道
其它IPC机制
练习:实现简单的Shell
31. Shell脚本
Shell的历史
Shell如何执行命令
执行交互式命令
执行脚本
Shell的基本语法
变量
文件名代换(Globbing):* ? []
命令代换:`或 $()
算术代换:$(())
转义字符\
单引号
双引号
bash启动脚本
作为交互登录Shell启动,或者使用--login参数启动
以交互非登录Shell启动
非交互启动
以sh命令启动
Shell脚本语法
条件测试:test [
if/then/elif/else/fi
case/esac
for/do/done
while/do/done
位置参数和特殊变量
函数
Shell脚本的调试方法
32. 正则表达式
引言
基本语法
sed
awk
练习:在C语言中使用正则表达式
33. 信号
信号的基本概念
产生信号
通过终端按键产生信号
调用系统函数向进程发信号
由软件条件产生信号
阻塞信号
信号在内核中的表示
信号集操作函数
sigprocmask
sigpending
捕捉信号
内核如何实现信号的捕捉
sigaction
pause
可重入函数
sig_atomic_t类型与volatile限定符
竞态条件与sigsuspend函数
关于SIGCHLD信号
34. 终端、作业控制与守护进程
终端
终端的基本概念
终端登录过程
网络登录过程
作业控制
Session与进程组
与作业控制有关的信号
守护进程
35. 线程
线程的概念
线程控制
创建线程
终止线程
线程间同步
mutex
Condition Variable
Semaphore
其它线程间同步机制
编程练习
36. TCP/IP协议基础
TCP/IP协议栈与数据包封装
以太网(RFC 894)帧格式
ARP数据报格式
IP数据报格式
IP地址与路由
UDP段格式
TCP协议
段格式
通讯时序
流量控制
37. socket编程
预备知识
网络字节序
socket地址的数据类型及相关函数
基于TCP协议的网络程序
最简单的TCP网络程序
错误处理与读写控制
把client改为交互式输入
使用fork并发处理多个client的请求
setsockopt
使用select
基于UDP协议的网络程序
UNIX Domain Socket IPC
练习:实现简单的Web服务器
基本HTTP协议
执行CGI程序
A. 字符编码
ASCII码
Unicode和UTF-8
在Linux C编程中使用Unicode和UTF-8
B. 编译开发工具小结
gcc常用选项
gcc常见错误信息
binutils常用命令
C. GNU Free Documentation License Version 1.3, 3 November 2008
参考书目
索引

插图清单

1.1. 编译执行过程
1.2. 解释执行过程
2.1. 在纸上表示变量
3.1. 函数调用的执行顺序
5.1. 函数的分层设计
5.2. factorial(3)的调用过程
7.1. 复数
7.2. 结构体传参
7.3. 数据抽象
8.1. 数组count
8.2. 字符串
8.3. 多维数组
11.1. 扑克牌的插入排序
11.2. Θ-notation
11.3. 1/2-3/n
11.4. 归并排序调用过程
12.1. 用堆栈实现倒序打印
12.2. 深度优先搜索
12.3. 广度优先搜索的队列数据结构
12.4. 广度优先搜索
12.5. 环形队列
14.1. 1-bit Full Adder
14.2. 4-bit Ripple Carry Adder
15.1. 整数常量的类型
17.1. 邮箱的地址
17.2. 访问内存读数据的过程
17.3. 设备
17.4. 物理地址
17.5. 虚拟地址
17.6. 处理器模式
17.7. Memory Hierarchy
18.1. ELF文件
18.2. 文件和加载地址的对应关系
19.1. 函数栈帧
19.2. gcc命令的选项
19.3. C程序的链接过程
19.4. 数组的存储布局
19.5. 结构体的存储布局
19.6. Bit Field的存储布局
20.1. 多目标文件的链接
20.2. 为什么要包含头文件而不是.c文件
20.3. 间接寻址
20.4. 进程地址空间
20.5. 进程地址空间是独立的
20.6. 不连续的PA可以映射为连续的VA
20.7. 换页
22.1. Makefile的依赖关系图
23.1. 指针的基本概念
23.2. char *指针的值赋给int *指针
23.3. 指针与数组
23.4. argv指针数组
23.5. C语言类型总结
23.6. 链表
24.1. strcpy(3)
24.2. strcpy(3)
24.3. strcpy(3)
24.4. strcpy(3)
24.5. 简单的mallocfree实现
24.6. myprintf函数的参数布局
25.1. C标准I/O库的缓存
28.1. 库函数与系统调用的层次关系
28.2. 文件描述符表
28.3. 重定向之后的文件描述符表
28.4. mmap函数
29.1. 文件系统的表示和存储
29.2. ext2文件系统的总体存储布局
29.3. 超级块
29.4. 块组描述符
29.5. 根目录的inode
29.6. 根目录的数据块
29.7. 数据块的寻址
29.8. VFS
29.9. dup/dup2示例程序
30.1. fork/exec
30.2. 进程地址空间
30.3. 环境变量
30.4. fork
30.5. exec函数族
30.6. 进程间通信
30.7. 管道
31.1. Shell脚本的执行过程
33.1. 信号在内核中的表示示意图
33.2. 信号的捕捉
33.3. 不可重入函数
34.1. 终端设备模块
34.2. 终端缓冲
34.3. 伪终端
34.4. Session与进程组
35.1. 并行访问冲突
35.2. 哲学家问题
36.1. TCP/IP协议栈
36.2. TCP/IP通讯过程
36.3. TCP/IP数据包的封装
36.4. 跨路由器通讯过程
36.5. Multiplexing过程
36.6. 以太网帧格式
36.7. ARP数据报格式
36.8. IP数据报格式
36.9. IP地址类
36.10. loopback设备
36.11. UDP段格式
36.12. TCP段格式
36.13. TCP连接建立断开
36.14. 滑动窗口
37.1. sockaddr数据结构
37.2. TCP协议通讯流程
37.3. 建立连接的过程
37.4. 关闭连接的过程
37.5. UDP通讯流程
A.1. ASCII码表
A.2. IBM的扩展ASCII码表
A.3. ISO-8859-1

表格清单

1.1. 同一个语句的三种表示
2.1. C标准规定的转义字符
4.1. 关系运算符和相等性运算符
4.2. AND的真值表
4.3. OR的真值表
4.4. NOT的真值表
10.1. gdb基本命令1
10.2. gdb基本命令2
10.3. gdb基本命令3
14.1. XOR的真值表
14.2. NAND的真值表
14.3. NOR的真值表
14.4. 1-bit Full Adder的真值表
15.1. ILP32和LP64
15.2. 如何做类型转换
17.1. Memory Hierarchy
18.1. 目标文件的布局
18.2. 一个4字节整数的字节序
20.1. Storage Class关键字对函数声明的作用
20.2. Storage Class关键字对变量声明的作用
20.3. Man Page的Section
24.1. 传入参数示例:void func(const unit_t *p);
24.2. 传出参数示例:void func(unit_t *p);
24.3. Value-result参数示例:void func(unit_t *p);
24.4. 通过参数分配内存示例:void alloc_unit(unit_t **pp); void free_unit(unit_t *p);
24.5. 返回指向已分配内存的指针示例:unit_t *func(void);
24.6. 动态分配内存并返回指针示例:unit_t *alloc_unit(void); void free_unit(unit_t *p);
24.7. 回调函数示例:void func(void (*f)(void *), void *p);
29.1. 目录中的文件类型编码
31.1. 通配符
31.2. 测试命令
31.3. 带与、或、非的测试命令
31.4. 常用的位置参数和特殊变量
32.1. 字符类
32.2. 数量限定符
32.3. 位置限定符
32.4. 其它特殊字符
32.5. 常用的sed命令
32.6. awk常用的内建变量
33.1. how参数的含义
36.1. 划分子网的例子1
36.2. 划分子网的例子2
37.1. client和server的socket状态

范例清单

1.1. Hello World
2.1. 带更多注释的Hello World
3.1. 在C语言中使用数学函数
3.2. 最简单的自定义函数
3.3. 较简单的自定义函数
3.4. 带参数的自定义函数
3.5. 全局变量
3.6. 作用域
3.7. 验证局部变量存储空间的分配和释放
4.1. switch语句
4.2. 缺break的switch语句
5.1. distance函数
6.1. 求1-100的素数
6.2. 用嵌套循环求1-100的素数
6.3. 打印小九九
7.1. 定义和访问结构体
8.1. 生成并打印随机数
8.2. 多维字符数组
8.3. 剪刀石头布
9.1. 缺少缩进和空白的代码
10.1. 函数调试实例
10.2. 断点调试实例
10.3. 观察点调试实例
10.4. 段错误调试实例一
10.5. 段错误调试实例二
11.1. 插入排序
11.2. 归并排序
11.3. 线性查找
11.4. 折半查找
11.5. 带有测试框架的折半查找
12.1. 用堆栈实现倒序打印
12.2. 用递归实现倒序打印
12.3. 用深度优先搜索解迷宫问题
12.4. 用广度优先搜索解迷宫问题
18.1. 最简单的汇编程序
18.2. 求一组数的最大值的汇编程序
19.1. 研究函数的调用过程
23.1. 打印命令行参数
28.1. 汇编程序的Hello world
28.2. 阻塞读终端
28.3. 非阻塞读终端
28.4. 非阻塞读终端和等待超时
28.5. 用fcntl改变File Status Flag
29.1. 递归列出目录中的文件列表
29.2. dup和dup2示例程序
30.1. 打印环境变量
30.2. 修改环境变量
30.3. fork
30.4. upper
30.5. wrapper
30.6. waitpid
30.7. 管道
31.1. 简单的Shell脚本
33.1. alarm
33.2. mysleep
34.1. 查看终端对应的设备文件名
34.2. 创建守护进程