复制成功
  • 图案背景
  • 纯色背景

笔记

  • 2019-11-16
    为大人带来形象的羊生肖故事来历 为孩子带去快乐的生肖图画故事阅读
    谈谈怎样学好数学_苏步青-中学生文库
rzchina1

上传于:2014-10-24

粉丝量:0

该文档贡献者很忙,什么也没留下。



Linux C程序设计王者归来试读

下载积分:0

内容提示: 目 录 第 1 篇 Linux 下 C 语言基础 第 1 章 Linux 简介( 教学视频: 16 分钟) 1. 1 GNU 简介 2 1. 2 Linux 简介 2 1. 2. 1 Linux 发展史 1. 2. 2 Linux 发行版 1. 2. 3 Linux 内核版本 5 1. 2. 4 Linux 与 UNIX 的关系 1. 2. 5 Linux 在服务器方面的发展 5 1. 2. 6 Linux 在嵌入式系统方面的发展 1. 2. 7 Linux 在桌面系统方面的发展 1. 3 Linux 环境下的其他编程语言 1. 3. 1 C++ 7 1. 3. 2 Java 9 1. 3. 3 Perl 11 1. 3. 4 Python 12 1. 3. 5 Ruby 13 1. 3. 6 PHP 13 第 2 章 控制结构( 教学视频: 56 分钟) 16 2. 1 goto 语句...

文档格式:PDF| 浏览次数:13| 上传日期:2014-10-24 11:10:09| 文档星级:
目 录 第 1 篇 Linux 下 C 语言基础 第 1 章 Linux 简介( 教学视频: 16 分钟) 1. 1 GNU 简介 2 1. 2 Linux 简介 2 1. 2. 1 Linux 发展史 1. 2. 2 Linux 发行版 1. 2. 3 Linux 内核版本 5 1. 2. 4 Linux 与 UNIX 的关系 1. 2. 5 Linux 在服务器方面的发展 5 1. 2. 6 Linux 在嵌入式系统方面的发展 1. 2. 7 Linux 在桌面系统方面的发展 1. 3 Linux 环境下的其他编程语言 1. 3. 1 C++ 7 1. 3. 2 Java 9 1. 3. 3 Perl 11 1. 3. 4 Python 12 1. 3. 5 Ruby 13 1. 3. 6 PHP 13 第 2 章 控制结构( 教学视频: 56 分钟) 16 2. 1 goto 语句 16 2. 1. 1 C 语言中的无条件跳转 2. 1. 2 使用 goto 语句进行出错处理 2. 1. 3 出错处理的一般模型 2. 2 C 语言中的分支结构 20 2. 2. 1 分支结构的翻译 20 2. 2. 2 使用 goto 语句实现分支结构 2. 3 短路计算 22 2. 3. 1 短路计算 22 2. 3. 2 &&运算的短路计算 2. 3. 3 | | 运算的短路计算 2. 4 C 语言中的循环结构 26 2. 4. 1 while 循环 2. 4. 2 do…while 循环 28 2. 4. 3 for 循环 30 2. 5 switch 语句 2. 5. 1 switch 语句的应用 2. 5. 2 使用 goto 语句实现 switch 语句 2. 6 优化控制结构 2. 6. 1 表达式优化——使用替换程序中的乘除法 2. 6. 2 表达式优化——常量折叠 35 2. 6. 3 表达式优化——使用数学公式 2. 6. 4 表达式优化——存储问题 37 2. 6. 5 分支优化——改变判断顺序 2 2 4 5 6 6 7 16 17 19 22 23 24 26 32 32 33 34 34 36 38 2. 6. 6 分支优化——使用 switch 语句 2. 6. 7 循环优化——一次性计算 42 第 3 章 C 语言中的函数( 教学视频: 56 分钟) 3. 1 函数的本质 46 3. 2 变量的作用域和生命期 3. 2. 1 全局变量 47 3. 2. 2 局部变量 48 3. 3 变量的初始值 3. 3. 1 全局变量的初始值 3. 3. 2 局部变量的初始值 3. 4 与函数有关的优化 51 3. 4. 1 函数调用与程序优化 3. 4. 2 变量存储优化 3. 5 编写多文件程序——变量的存储类别 56 3. 5. 1 存储类别 56 3. 5. 2 static 变量的作用——改变变量的生命期 56 3. 5. 3 static 变量的作用——实现封装和模块化设计 3. 6 编写多文件的程序——链接的作用 3. 6. 1 链接多个文件 61 3. 6. 2 链接时符号解析规则 3. 6. 3 链接规则的应用 63 3. 7 可变参数 66 3. 7. 1 可变参数的概念 66 3. 7. 2 实现一个简单的可变参数的函数 3. 7. 3 可变参数实例 69 3. 7. 4 关于 printf() 函数的疑问——缺少整型参数 74 3. 7. 5 关于 printf() 函数的疑问——缺少字符串地址参数 75 第 4 章 C 语言中的指针与字符串( 教学视频: 48 分钟) 78 4. 1 sizeof 运算符 78 4. 1. 1 sizeof 运算符的应用——得到内置类型的大小 4. 1. 2 sizeof 运算符的应用——得到复合类型的大小 4. 2 指针的应用 80 4. 2. 1 指针与别名陷阱 80 4. 2. 2 数组的指针 82 4. 2. 3 指针的指针 83 4. 2. 4 指针与参数传递 85 4. 2. 5 指针类型的意义 90 4. 2. 6 void*型指针 91 4. 3 函数的指针 94 4. 3. 1 C 语言中的函数指针 4. 3. 2 函数指针的应用——回调函数 4. 3. 3 函数指针数组 102 4. 4 字符串 103 4. 4. 1 字符串与字符数组 41 46 47 50 50 50 52 54 58 61 63 67 78 79 94 97 103 4. 4. 2 字符串与指针 4. 4. 3 限定修饰符 const 106 4. 4. 4 const 关键字修饰指针——在指针定义之前 107 4. 4. 5 const 关键字修饰指针——在指针定义之中 107 4. 4. 6 const 关键字修饰指针——在指针定义之前和定义之中 4. 4. 7 使用 const 关键字的意义 108 第 5 章 C 语言的高级技术( 教学视频: 30 分钟) 111 5. 1 结构体与共同体 111 5. 1. 1 结构体中成员变量的存储分布 5. 1. 2 内存对齐 112 5. 2 位运算 115 5. 2. 1 掩码运算 115 5. 2. 2 不安全的位运算 117 5. 2. 3 异或运算的特性 119 5. 2. 4 移位运算的陷阱 121 5. 2. 5 移位运算的实例 122 5. 3 预处理 124 5. 3. 1 常用的代码组织形式 5. 3. 2 调试开关 129 5. 4 C99 新标准关键字详解 5. 4. 1 inline 关键字的概念 5. 4. 2 inline 关键字实例 5. 4. 3 inline 关键字使用总结 5. 4. 4 restrict 关键字的概念 5. 4. 5 restrict 关键字的应用 第 2 篇 C 语言开发环境 第 6 章 vi 编辑器( 教学视频: 44 分钟) 136 6. 1 vi 编辑器入门 136 6. 1. 1 vi 简介 136 6. 1. 2 vi 的工作模式 136 6. 2 vi 一般操作 137 6. 2. 1 进入 vi 137 6. 2. 2 文本插入操作 140 6. 2. 3 文本删除操作 141 6. 2. 4 文本复制操作 143 6. 2. 5 撤销命令 144 6. 2. 6 重复命令 145 6. 2. 7 退出 vi 145 6. 3 vi 的增强操作 147 6. 3. 1 替换命令 147 6. 3. 2 光标移动 148 6. 3. 3 按行移动光标 148 6. 3. 4 按字移动光标 149 6. 3. 5 按句移动光标 150 104 107 111 124 130 131 131 132 133 134 6. 3. 6 按段移动光标 6. 3. 7 文本行的移动 6. 3. 8 文本的异行移动 153 6. 3. 9 屏幕滚动 153 6. 3. 10 查找命令 6. 3. 11 替换命令 6. 3. 12 窗口的切分 6. 3. 13 设置环境 第 7 章 gcc 编译器( 教学视频: 27 分钟) 7. 1 初探 gcc 编译器 7. 1. 1 从经典的 hello world 开始 7. 1. 2 gcc 编译流程 7. 2 gcc 常用选项 7. 2. 1 gcc 常用选项汇总 161 7. 2. 2 -c 选项 161 7. 2. 3 -S 选项 162 7. 2. 4 -E 选项 163 7. 2. 5 -o 选项 164 7. 2. 6 -I 选项 165 7. 2. 7 -g 选项 165 7. 3 链接原理 165 7. 3. 1 链接器的任务 7. 3. 2 目标文件 165 7. 3. 3 ELF 格式的可重定位目标文件 7. 3. 4 目标文件中的符号表 7. 3. 5 重定位的概念 7. 3. 6 符号的重定位信息 7. 4 关于库 173 7. 4. 1 使用库的优势 7. 4. 2 静态库的概念 7. 4. 3 创建静态库 7. 4. 4 使用静态库 7. 4. 5 动态库的概念 7. 4. 6 创建动态库 7. 4. 7 使用动态库 7. 5 gcc 工具链 181 第 8 章 构建 makefile 文件( 教学视频: 46 分钟) 8. 1 makefile 文件入门 183 8. 1. 1 makefile 文件的组成内容 183 8. 1. 2 makefile 文件包含 8. 1. 3 make 工具的退出码 8. 2 书写 makefile 规则 185 8. 2. 1 使用基本规则 185 8. 2. 2 使用隐式规则 186 151 151 154 155 157 157 159 159 159 160 160 165 167 168 172 172 173 175 175 176 177 178 180 183 183 184 8. 2. 3 使用伪目标 8. 2. 4 使用通配符 8. 2. 5 搜索源文件 8. 3 使用命令 190 8. 3. 1 显示命令 190 8. 3. 2 执行命令 190 8. 3. 3 命令出错 192 8. 4 使用变量 192 8. 4. 1 使用普通变量 8. 4. 2 变量中的变量 8. 4. 3 追加变量的值 8. 4. 4 自动化变量 8. 5 使用条件判断 8. 5. 1 条件表达式 8. 5. 2 表达式实例 8. 6 使用函数 200 8. 6. 1 函数调用的语法 200 8. 6. 2 字符串处理函数 201 8. 6. 3 文件名操作函数 206 8. 6. 4 foreach 函数 8. 6. 5 if 函数 210 8. 6. 6 call 函数 8. 6. 7 origin 函数 8. 6. 8 shell 函数 8. 7 makefile 实例 8. 7. 1 makefile 实例——项目中的总 makefile 8. 7. 2 makefile 实例——makefile 模板 214 第 9 章 gdb 调试器( 教学视频: 27 分钟) 9. 1 列出源程序 216 9. 1. 1 不带参数的 list 命令 9. 1. 2 带一个参数的 list 命令 217 9. 1. 3 带两个参数的 list 命令 217 9. 2 运行程序的命令 9. 3 操作断点的命令 9. 3. 1 设置断点 220 9. 3. 2 显示当前 gdb 的断点信息 222 9. 3. 3 删除指定的断点 223 9. 3. 4 禁止或启用断点 223 9. 3. 5 清除断点 223 9. 3. 6 观察点 224 9. 3. 7 设置断点实例 9. 4 查看运行时数据 9. 4. 1 数据观察命令 9. 4. 2 对程序中函数的调用 187 188 189 192 194 196 197 197 197 199 209 210 211 211 212 212 216 216 218 220 225 228 229 230 9. 4. 3 查看表达式的值 231 9. 4. 4 查看数组的值 9. 4. 5 变量的输出格式 232 9. 4. 6 查看内存 233 9. 4. 7 自动显示变量 9. 4. 8 设置显示选项 9. 4. 9 显示变量的历史记录 9. 4. 10 查看寄存器 9. 4. 11 查看使用 gdb 环境变量 240 9. 4. 12 查看数据实例 9. 5 改变程序的执行 9. 5. 1 修改变量的值 9. 5. 2 跳转执行 246 9. 5. 3 信号的产生及处理 9. 5. 4 强制调用函数 9. 5. 5 强制函数返回 9. 6 gdb 高级应用 9. 6. 1 产生 core 文件 9. 6. 2 跟踪栈上数据 9. 6. 3 绑定运行进程 9. 6. 4 源文件搜索 9. 6. 5 机器语言工具 9. 6. 6 其他有用的调试命令 第 3 篇 Linux 进程操作 第 10 章 进程环境( 教学视频: 44 分钟) 10. 1 程序的启动和退出 258 10. 1. 1 在 shell 中启动一个程序 258 10. 1. 2 加载一个程序 10. 1. 3 加载地址 10. 1. 4 退出程序 10. 1. 5 进程终止处理函数 10. 2 Linux 进程内存管理 10. 2. 1 数据的内部存储 263 10. 2. 2 C 程序的存储布局——代码段 10. 2. 3 C 程序的存储布局——数据段和缓冲段 10. 2. 4 C 程序的存储布局——栈 268 10. 2. 5 C 程序的存储布局——堆 269 10. 2. 6 常量的存储 10. 2. 7 动态内存管理 10. 2. 8 动态内存分配的深入研究 273 10. 3 shell 环境 10. 3. 1 命令行参数 10. 3. 2 得到程序文件名 278 10. 3. 3 环境变量 231 233 237 239 239 241 245 246 248 248 249 249 250 250 252 254 254 255 258 259 260 261 262 263 265 267 270 271 276 277 278 10. 3. 4 得到指定的环境变量 10. 3. 5 设置一个环境变量 10. 3. 6 得到进程结束状态 10. 3. 7 使用 errno 调试程序 10. 3. 8 输出错误原因 10. 4 全局跳转 289 10. 4. 1 局部跳转的局限性 10. 4. 2 使用全局跳转 10. 4. 3 使用全局跳转代替局部跳转 第 11 章 进程控制( 教学视频: 45 分钟) 11. 1 进程标识符 11. 1. 1 进程 ID 295 11. 1. 2 进程中重要的 ID 值 11. 2 进程操作 297 11. 2. 1 创建一个进程 11. 2. 2 父子进程的共享资源 11. 2. 3 fork() 函数的出错情况 302 11. 2. 4 创建一个共享空间的子进程 11. 2. 5 在函数内部调用 vfork() 函数 11. 2. 6 退出进程 11. 2. 7 使用 exit() 函数检查进程出错信息 11. 2. 8 exit() 函数与内核函数的关系 11. 2. 9 设置进程所有者 307 11. 2. 10 调试多进程——设置跟踪流 11. 2. 11 调试多进程——使用 gdb 的 attach 命令 309 11. 3 执行程序 310 11. 3. 1 执行一个新程序 310 11. 3. 2 执行解释器文件 313 11. 3. 3 在程序中执行 shell 命令 315 11. 3. 4 实现 system() 函数 11. 4 关系操作 318 11. 4. 1 等待进程退出 318 11. 4. 2 等待指定的进程 321 11. 4. 3 僵尸进程的概念 323 11. 4. 4 产生一个僵尸进程 11. 4. 5 避免僵尸进程的产生 11. 4. 6 输出进程统计信息 第 12 章 时间和日历历程( 教学视频: 7 分钟) 330 12. 1 系统时间 330 12. 2 日历时间 332 第 13 章 信号及信号处理( 教学视频: 48 分钟) 335 13. 1 信号的基础 335 13. 1. 1 信号的基本概念 335 13. 1. 2 产生信号 336 280 280 285 286 288 289 291 292 295 295 296 298 299 303 304 305 306 307 309 317 324 325 327 13. 1. 3 处理信号 13. 1. 4 常用信号的使用方法 13. 2 信号的影响 13. 2. 1 重入 13. 2. 2 原子操作 13. 2. 3 中断系统调用 13. 3 信号处理函数 13. 3. 1 设置信号处理函数 13. 3. 2 发送信号 13. 3. 3 向进程本身发送信号 13. 3. 4 设置 Linux 定时器 13. 3. 5 定时等待 I/O 13. 3. 6 挂起进程 13. 3. 7 进程休眠 13. 4 信号集与屏蔽信号 356 13. 4. 1 信号集和信号集处理函数 356 13. 4. 2 屏蔽信号 13. 4. 3 处理未决信号 13. 4. 4 高级信号注册函数 13. 4. 5 信号选项实例——SA_NOCLDWAIT 选项 13. 4. 6 信号选项实例——SA_NODEFER 选项 13. 4. 7 信号选项实例——SA_RESETHAND 选项 第 14 章 进程间通信( 教学视频: 52 分钟) 14. 1 进程间通信概述 14. 1. 1 进程间通信简介 368 14. 1. 2 进程间通信的难点 14. 1. 3 IPC 的多种方式 369 14. 2 管道 370 14. 2. 1 管道的概念 14. 2. 2 匿名半双工管道 370 14. 2. 3 匿名半双工管道的读写操作 14. 2. 4 创建管道的标准库函数 375 14. 3 FIFO 管道 377 14. 3. 1 FIFO 的概念 14. 3. 2 创建 FIFO 14. 3. 3 FIFO 的读写操作 379 14. 3. 4 FIFO 的缺点 14. 4 System V IPC/POSIX IPC 383 14. 4. 1 IPC 对象的概念 383 14. 4. 2 IPC 对象的问题 385 14. 4. 3 IPC 对象系统命令 14. 5 共享内存 386 14. 5. 1 共享内存的概念 386 14. 5. 2 共享内存的创建 387 336 337 338 339 341 343 343 343 347 348 349 350 352 353 358 360 362 363 364 366 368 368 369 370 371 377 378 382 385 14. 5. 3 共享内存的操作 388 14. 5. 4 共享内存使用注意事项 390 14. 6 信号量 391 14. 6. 1 信号量的概念 14. 6. 2 信号量的创建 14. 6. 3 信号量集的操作 394 14. 7 消息队列 396 14. 7. 1 消息队列的概念 396 14. 7. 2 创建消息队列 14. 7. 3 读写消息队列 第 15 章 线程( 教学视频: 34 分钟) 15. 1 线程与进程 15. 1. 1 线程的概念 15. 1. 2 线程的优势 15. 2 线程标识符 15. 3 线程基本操作 15. 3. 1 创建线程 15. 3. 2 向线程体函数传递参数 407 15. 3. 3 线程访问资源的限制 15. 3. 4 终止线程 15. 3. 5 正确得到线程退出信息的方法 15. 3. 6 取消一个线程的运行 15. 3. 7 线程退出函数 第 16 章 线程高级操作( 教学视频: 28 分钟) 16. 1 线程同步——使用互斥量 423 16. 1. 1 初始化与销毁互斥量 16. 1. 2 得到与释放互斥量 16. 1. 3 使用互斥量的实例 16. 2 线程同步——使用读写锁 430 16. 2. 1 初始化与销毁读写锁 16. 2. 2 得到与释放互斥锁 16. 2. 3 使用互斥量与读写锁的比较 16. 3 线程属性 438 16. 3. 1 创建和销毁属性结构 16. 3. 2 线程的分离状态 439 16. 3. 3 分离一个已经创建的线程 440 16. 3. 4 线程栈的属性 第 4 篇 Linux 文件操作 第 17 章 文件 I/O( 教学视频: 71 分钟) 446 17. 1 文件描述符的概念 446 17. 2 文件 I/O 操作 17. 2. 1 打开一个文件 17. 2. 2 打开文件的出错情况 17. 2. 3 关闭一个文件 391 392 397 399 403 403 403 404 405 405 405 409 412 415 417 420 423 423 424 425 430 431 432 438 442 446 446 450 450 17. 2. 4 创建一个新文件 451 17. 2. 5 文件定位 17. 2. 6 文件截短 17. 2. 7 清空一个文件 17. 2. 8 文件的读写操作 460 17. 2. 9 文件同步 17. 3 文件描述符操作 17. 3. 1 复制文件描述符 465 17. 3. 2 I/O 重定向概念及其应用 466 17. 3. 3 控制文件 17. 3. 4 修改打开文件的文件状态 470 17. 4 非阻塞文件 I/O 17. 4. 1 非阻塞 I/O 的概念 17. 4. 2 以非阻塞方式打开文件 474 17. 4. 3 将一个打开文件设置为非阻塞方式 475 17. 5 内存映射 I/O 17. 5. 1 内存映射 I/O 的概念 17. 5. 2 创建一个内存映射 17. 5. 3 撤销一个内存映射 17. 5. 4 内存映射同步 17. 5. 5 更改内存映射的权限 第 18 章 文件管理( 教学视频: 42 分钟) 18. 1 文件状态 487 18. 2 文件类型 488 18. 3 文件权限 490 18. 3. 1 访问权限 18. 3. 2 设置用户 ID 位和设置组 ID 位 18. 3. 3 设置用户 ID 位的作用 18. 3. 4 使用设置用户 ID 位的实例 18. 4 文件权限操作 18. 4. 1 测试文件访问权限 18. 4. 2 设置用户 ID 位与文件权限测试 18. 4. 3 使用文件权限屏蔽字 18. 4. 4 在 shell 中使用文件权限屏蔽字 500 18. 4. 5 在程序中使用文件权限屏蔽字 18. 4. 6 改变文件访问权限 18. 4. 7 在 shell 中改变文件权限 503 18. 4. 8 在程序中改变文件权限 504 18. 4. 9 改变一个打开的文件权限 505 18. 5 文件系统结构 18. 6 文件的硬链接 18. 6. 1 创建一个硬链接 513 18. 6. 2 删除一个硬链接 515 18. 7 改变文件所有者 453 455 458 463 465 469 473 473 477 477 478 480 482 485 487 491 492 493 494 496 497 499 499 501 502 509 513 519 18. 8 操作文件的时间 第 19 章 目录操作( 教学视频: 30 分钟) 19. 1 目录的权限 19. 1. 1 目录的访问权限 526 19. 1. 2 测试目录的访问权限 19. 2 目录操作 528 19. 2. 1 创建一个目录 19. 2. 2 删除一个目录 19. 2. 3 目录项结构 19. 2. 4 打开与关闭目录 531 19. 2. 5 读目录 531 19. 2. 6 读目录操作的实例——实现自己的 ls 命令 534 19. 2. 7 得到目录文件的读写位置 536 19. 2. 8 定位目录 537 19. 2. 9 回卷目录文件 539 19. 3 进程的工作目录 540 19. 3. 1 工作目录的概念 540 19. 3. 2 改变进程的工作目录 19. 3. 3 得到进程的当前工作目录 542 19. 3. 4 子进程工作目录对父进程的影响 545 第 20 章 特殊文件( 教学视频: 32 分钟) 20. 1 符号链接操作 548 20. 1. 1 符号链接 548 20. 1. 2 符号链接的功能 549 20. 1. 3 创建符号链接 549 20. 1. 4 创建指向不存在文件的符号链接 550 20. 1. 5 读写符号链接的目的文件 551 20. 1. 6 读写多重符号链接的目的文件 20. 1. 7 读写符号链接 555 20. 1. 8 取得符号链接的状态 20. 1. 9 更改符号链接的所有者 562 20. 2 proc 文件系统 566 20. 2. 1 proc 文件系统 566 20. 2. 2 常用的内核信息 567 20. 2. 3 读取内核信息 568 20. 2. 4 进程状态信息 575 20. 2. 5 读取进程状态 576 20. 2. 6 proc 文件系统编程实例——实现自己的 ps 命令 第 21 章 基于流的 I/O( 教学视频: 30 分钟) 21. 1 流与缓冲 581 21. 2 基于流的文件操作 583 21. 2. 1 打开和关闭流 583 21. 2. 2 文件内容的冲洗 585 21. 2. 3 以字符为单位读写数据 587 522 526 526 527 528 529 530 541 548 553 558 578 581 21. 2. 4 以行为单位读写数据 21. 2. 5 gets() 函数的漏洞 21. 2. 6 逐行读取文件内容 21. 2. 7 输出 XML 形式的配置文件 591 21. 2. 8 读写数据块 21. 2. 9 文件读写实例——实现 cp 命令 21. 2. 10 文件读写实例——字符统计 21. 2. 11 文件读写实例——目录下所有文件的字符统计 第 5 篇 Linux 网络编程 第 22 章 TCP 和 UDP 协议( 教学视频: 22 分钟) 602 22. 1 计算机网络的体系结构简介 602 22. 1. 1 计算机网络体系结构简介 602 22. 1. 2 OSI 参考模型中各层功能描述 22. 1. 3 TCP/IP 参考模型的各层功能描述 603 22. 1. 4 面向连接服务和无连接服务 22. 2 客户/服务器方式简介 22. 3 用户数据报协议 UDP 22. 3. 1 用户数据报的格式 22. 3. 2 用户数据报检验和的计算 606 22. 3. 3 UDP 用户数据报的特性及用途 22. 4 传输控制协议 TCP 608 22. 4. 1 TCP 简介 608 22. 4. 2 TCP 报文段的首部 22. 4. 3 TCP 数据编号与确认 22. 4. 4 TCP 的流量控制与拥塞控制 22. 4. 5 TCP 的运输连接管理 第 23 章 网络编程基础( 教学视频: 50 分钟) 23. 1 套接字概念 614 23. 2 准备工作 615 23. 2. 1 字节序 615 23. 2. 2 地址格式 616 23. 2. 3 地址形式转换 617 23. 2. 4 获得主机信息 619 23. 2. 5 地址映射 620 23. 3 套接字基础编程 621 23. 3. 1 建立和销毁套接字描述符 621 23. 3. 2 地址绑定 623 23. 3. 3 建立一个连接 624 23. 3. 4 最简单的网络应用程序——服务器端 23. 3. 5 最简单的网络应用程序——客户端 629 23. 3. 6 使用文件读写函数读写套接字 23. 3. 7 使用完整读写函数的网络应用程序 634 23. 3. 8 面向连接的数据传输 23. 3. 9 面向连接的网络应用程序——服务器端 588 589 590 593 594 596 597 603 604 604 605 605 607 608 609 610 612 614 626 631 637 638 23. 3. 10 面向连接的网络应用程序——客户端 23. 3. 11 无连接的数据传输 23. 3. 12 无连接的网络应用程序——服务器端 23. 3. 13 无连接的网络应用程序——客户端 23. 4 非阻塞套接字 23. 4. 1 非阻塞网络应用程序——服务器端 649 23. 4. 2 非阻塞网络应用程序——客户端 651 第 24 章 网络编程进阶( 教学视频: 32 分钟) 24. 1 套接字编程深入 24. 1. 1 bind 函数的作用 654 24. 1. 2 并发服务器 24. 1. 3 UDP 协议的 connect() 函数应用 24. 2 多路选择 I/O 24. 2. 1 多路选择 I/O 的概念 24. 2. 2 实现多路选择 I/O 24. 2. 3 使用多路选择 I/O 处理多个连接 663 24. 2. 4 屏蔽信号的多路选择 I/O 666 24. 2. 5 对比 select() 函数和 pselect() 函数 24. 2. 6 轮询 I/O 24. 3 非网络通信套接字 670 24. 3. 1 非命名 UNIX 域套接字 24. 3. 2 命名 UNIX 域套接字 24. 3. 3 UNIX 域套接字实例——服务器端 673 24. 3. 4 UNIX 域套接字实例——客户端 第 25 章 网络编程实例——实现文件传输程序( 教学视频: 18 分钟) 679 25. 1 程序设计 679 25. 1. 1 整体程序设计 679 25. 1. 2 客户端程序设计 679 25. 1. 3 服务器端程序设计 680 25. 2 文件传输程序的实现 683 25. 2. 1 客户端程序的实现——头文件 25. 2. 2 客户端程序的实现——程序主函数 684 25. 2. 3 客户端程序的实现——命令拆分函数 25. 2. 4 客户端程序的实现——命令处理模块 25. 2. 5 服务器端程序的实现——头文件 697 25. 2. 6 服务器端程序的实现——程序主函数 25. 2. 7 服务器端程序的实现——客户端命令处理模块 25. 3 测试和验证 706 第 26 章 网络编程实例——简单的 Web 服务器( 教学视频: 10 分钟) 707 26. 1 程序设计 707 26. 1. 1 整体程序设计 707 26. 1. 2 步骤 1——解析客户端发送的内容 708 641 643 644 646 649 654 654 655 657 660 660 661 667 669 671 672 677 683 686 687 698 699 26. 1. 3 步骤 2——寻找客户端需要的文件 709 26. 1. 4 步骤 3——将客户端信息返回 26. 2 Web 服务器的实现 710 26. 2. 1 Web 服务器程序的实现——头文件 710 26. 2. 2 Web 服务器程序的实现——程序主函数 26. 2. 3 Web 服务器程序的实现——请求处理模块 715 26. 3 测试和验证 719 第 6 篇 shell 脚本知识 第 27 章 shell 脚本基础( 教学视频: 32 分钟) 722 27. 1 编写最简单的 shell 脚本 722 27. 1. 1 使用 shell 脚本的原因 722 27. 1. 2 脚本内容 723 27. 1. 3 运行一段脚本 724 27. 2 shell 中的变量 724 27. 2. 1 用户自定义变量 725 27. 2. 2 引用变量 726 27. 2. 3 为表达式求值 727 27. 2. 4 变量的类型 729 27. 2. 5 操作自定义变量 730 27. 2. 6 位置变量 731 27. 2. 7 重新分配位置变量 27. 3 退出状态 734 27. 4 条件测试 736 27. 4. 1 测试文件状态 736 27. 4. 2 测试时使用逻辑操作符 738 27. 4. 3 字符串测试 739 27. 4. 4 测试数值 741 第 28 章 shell 脚本中的控制结构( 教学视频: 29 分钟) 743 28. 1 shell 脚本中的命令替换 28. 2 分支结构 744 28. 2. 1 if/else 分支结构 28. 2. 2 多重分支结构 745 28. 2. 3 使用 case 结构的分支语句 28. 2. 4 在 case 语句中使用逻辑表达式 28. 3 条件测试命令的值 750 28. 3. 1 测试整数与字符串 28. 3. 2 测试字符串和空值 28. 3. 3 测试未初始化的变量 28. 3. 4 测试“false” 的值 28. 4 循环结构 754 28. 4. 1 while 循环结构 754 28. 4. 2 until 循环结构 757 28. 4. 3 for 循环结构 759 28. 4. 4 break 和 continue 命令 762 709 711 732 743 744 747 749 751 752 752 753 28. 5 定义函数和使用函数 764 第 3 章 C 语言中的函数 函数是一种特殊的控制流程, 只有理解函数调用的本质才能够更好地理解程序中的存储类别和模块化程序设计的思想。 本章将详细介绍函数。 3.1 函数的本质 函数的代码存储在内存中的代码段中, 每个 C 语言程序都有一个代码段。 下面实例给出了 一个 C 语言函数和其翻译为汇编代码的形式。 说明: 函数的本质是一段二进制可执行代码, 这些代码是一些可以被机器直接执行的 指令。 程序清单 3-1 swap.c 实现两个整数交换位置并且相加的函数 01 int swap(int *a, int *b) 02 { 03 int tmp; 04 tmp = *a; 05 *a = *b; 06 *b = tmp; 07 return *a + *b; /* 返回二者的和 */ 08 } 在翻译为汇编语言时, 函数被翻译成为一段相对独立的汇编代码, 并且使用函数名作为标号, 表示此段代码的入口。 当程序中需要调用该函数时, 只需要跳转到这个标号处执行标号后面的函数代码就可以了。在内存中该函数机器指令的存储如图 3-1 所示。 /* 交换两个指针所指向的内容 */ /* 交换两个变量的值 */ 由此可知, 函数的本质是一段机器指令代码。而函数名的本质是一个标号, 该标号的值等于内存中存储函数代码的内存空间的首地址。 函数在调用 时会使进程空间中 的栈不断增长, 从当前进程空间中的栈顶的位置到函数保存返回地址的位置, 这块内存称为函数的栈帧。 所有函数中定义的局部变量都存储在函数的栈帧上。当函数结束调用时该块栈帧就消失了, 如图 3-2 所示。 图 3-1 函数调用的示意图 第 3 章 C 语言中的函数 · 47·     图 3-2 函数的栈帧 3.2 变量的作用 域和生命期 C 语言中每一个变量都有自己的作用域和生命期。 作用域表示可以引用该变量的代码区域, 生命期表示变量的存储空间所保存的时间。 如果以变量的生命期来分类的话, C 语言中的变量包括两种类型: 一种是全局变量, 一种是局部变量。 本节将详细介绍变量的作用域和生命期。 3.2.1 全局变量 全局变量也称为外部变量, 这种变量是在函数外部定义的。 因此它们不属于任何一个函数, 而是属于一个源程序文件。 其作用域从定义该变量的这一行开始, 一直可以持续到定义该变量的源程序文件的结束。 在该区间内所有的函数都可以引用该变量。 下面实例演示了 一个全局变量的使用。 该程序定义了 一个全局变量, 之后在两个函数中引用这个全局变量, 并分别计算这个全局变量和函数参数的和, 最后输出结果。 (1) 在 vi 编辑器中编辑该程序。 程序清单 3-2 global.c 全局变量的使用 01 #inlcude <stdio. h> 02 int add(int a, int b) 03 { 04 return a + b; /* 全局变量 b 的作用域达不到这里, 因此 add() 函数看不到全局 变量 b */ 05 } 06 int b = 10; /* 定义一个全局变量 b, 以后的函数可以应用这个全局变量 */ 07 int mul(int a) 08 { 09 return a * b; /* 在 mul() 函数中引用全局变量 b */ 10 } 11 int main(void) 12 { 13 int res1; /* 保存结果的临时变量 */ 第 1 篇 Linux 下 C 语言基础 · 48· 14 15 16 int res2; res1 = add(1, 5) ; res2 = mul(1) ; /* 调用 add() 函数 */ /* 调用 mul() 函数 */ 17 18 printf("res1 : %d, res2 : %d\n", res1, res2) ; printf("the global b : %d\n", b) ; /* 在 main() 函数中引用全局变量 b */ return 0; 19 20 } (2) 在 shell 中编译该程序。 $gcc global. c -o global (3) 在 shell 中运行该程序。 $. /global res1 : 6, res2 : 10 the global b : 10 在全局变量 b 定义之前先定义了 add()函数, 因此 add()函数不能引用这个全局变量。如果希望在 add()函数中也可以引用该变量, 则需要在 add()函数之前加上对全局变量 b 的声明, 之后 add()函数就可以引用该变量了 。 . . . int b; /* 添加对全局变量 b 的声明, 编译器会自动寻找该变量的定义 */ int add(int a) { return a + b; /* 声明之后可以在函数 add() 中引用该变量 */ } int b = 10; /* 定义该变量 */ . . . 3.2.2 局部变量 局部变量也称为内部变量, 其定义在函数或者复合语句的内部。 其作用域仅限于函数或者复合语句内, 离开该函数或者复合语句后就无法再引用该函数。 注意: 定义在复合语句内 部的变量同样是局部变量。 下面实例演示了 局部变量的使用。 该程序在复合语句内部定义了 一个局部变量 res, 该局部变量在复合语句之外不能引用。 因此在定义该变量的复合语句之外此变量就失效了 。 (1) 在 vi 编辑器中编辑该程序。 程序清单 3-3 local.c 演示使用局部变量 01 #include <stdio. h> 02 int add(int a, int b) 03 { 04 return a + b; /* add() 函数内部不能引用 main() 函数中定义的局部变量 05 */ 06 } 07 int main(void) 08 { 第 3 章 C 语言中的函数 · 49·09 int array[5] ; /* 局部变量数组 */ 10 11 12 13 14 int i=0; while(i < 5) { int res; res = add(i, 1) ; array[i] = res; /* res 定义在复合语句内部, 因此 res 也是一个局部变量, 在复合语句之外不能引用 */ /* 对数组每个元素赋值 */ /* 将结果保存在数组中 */ 15 16 17 14 } for(i = 0; i < 5; i++) printf("array[%d] : %d\n", i + 1, array[i] ) ; /* 输出每一个数组元 i++; 素 */ 15 16 } (2) 在 shell 中编译该程序。 $gcc local. c -o local (3) 在 shell 中执行该程序。 $. /local array[1] : 1 array[2] : 2 array[3] : 3 array[4] : 4 array[5] : 5 res 是一个局部变量, 在复合语句结束之后, 变量 res 同样不能再被该函数中的其他语句所引用。 也就是说 res 变量的作用域仅限于 while 语句的循环中。 return 0; 说明: 全局变量的生命期是整个程序, 而局部变量的生命期仅在函数调用未结束之前有效。 因此, 变量的作用域实际上是由变量的生命期来决定的, 如图 3-3 所示。      图 3-3 变量生命期示意图 第 1 篇 Linux 下 C 语言基础 · 50· 3.3 变量的初始值 变量在定义之后要占用存储空间, 如果未对变量进行初始化, 这时变量中的值是多少呢? 本节将介绍变量的初始值。 3.3.1 全局变量的初始值 对于全局变量来说, 如果一个全局变量未被初始化, 其初始值由编译器自动设置为 0。因此在使用一个全局变量时, 不需要考虑其初值问题, 直接使用就可以了 。 下面实例演示了 输出一个未初始化的全局变量的初始值。 该程序定义了 一个全局变量, 但是未对其进行初始化。 之后在程序中输出该变量的值, 以观察该变量的初始值。 (1) 在 vi 编辑器中编辑该程序。 程序清单 3-4 uninit_global.c 输出一个未初始化的全局变量的值 01 #incllude <stdio. h> 02 int a; /* 全局变量 */ 03 int main(void) 04 { 05 printf("the global : %d\n", a) ; /* 输出全局变量的值 */ 06 return 0; 07 } (2) 在 shell 中编译该程序。 $gcc uninit_global. c -o uninit_global (3) 在 shell 中运行该程序。 $. / uninit_global the global : 0 3.3.2 局部变量的初始值 局部变量存储在内存的堆栈中。 定义了 局部变量之后, 编译器不会将其初始化为 0,而是使用其占用的内存空间原有的值。 这时的值是一个随机值, 与本次程序无关, 如图 3-4所示。 因此, 如果对局部变量不初始化就直接引用是很危险的。 下面实例演示了 这种危险的用法。 该程序计算 1~10 之间整数的和。 在 main()函数中定义了一个局部变量 sum, 并使用它作为存储累加和的临时变量。 (1) 在 vi 编辑器中编辑该程序。 第 3 章 C 语言中的函数 · 51· 图 3-4 函数栈帧占用内存空间示意图 程序清单 3-5 sum.c 累加 1~10 之间整数的和 01 #include <stdio. h> 02 int main(void) 03 { 04 int i; 05 int sum; /* 定义一个局部变量, 该局部变量作为一个累加和的临时值 */ 06 for(i = 1; i <= 10; i++) 07 sum += i; 08 printf("the sum is : %d\n", sum) ; /* 输出运算结果 */ 09 return 0; 10 } (2) 在 shell 中编译该程序。 $gcc sum. c -o sum (3) 在 shell 中运行该程序。 $. /sum the sum is : -10926734 程序执行之后的结果和预料的大相径庭。 原因在于在使用 sum 作为累加和之前没有对sum 进行初始化, 所以 sum 的起始值并不是 0, 而是 sum 所在的存储单元中之前保存的一个无用值。 这个值是随机的, 可能是一个非常大的整数。 这样的累加造成了 sum 变量溢出。由此可知, 对一个局部变量进行初始化是多么重要。 /* 每次累加 i */ 注意: 如果不进行初始化, 这种错误不仅无法避免, 而且很难调试。 3.4 与函数有关的优化 函数作为程序的一个重要组成部分, 其优化作用是不容小视的。 本节将介绍有关函数 第 1 篇 Linux 下 C 语言基础 · 52· 的优化。 3.4.1 函数调用与程序优化 函数的作用是使代码模块性更强, 利于代码的修改和阅读, 并且可以有效地减小代码的体积。 但是函数的调用通常很费时, 一个函数调用的主要步骤有以下 4 步:  函数调用需要将参数压入堆栈。  函数调用需要保存寄存器的值。  函数调用需要保存返回地址。  函数调用会造成跳转。 以上 4 步操作中的前 3 个步骤都需要访问内存, 而最后 1 步造成一次跳转。 访问内存在计算机的操作中很消耗时间, 如果每次调用函数都需要大量的时间访问内存, 那么其执行速度慢是理所当然的。 因此从这个角度来讲, 函数可以说是一个时间换空间的例子。 说明: 由于函数调用 耗费时间, 因此在程序执行过程中应当减少函数的调用 , 这样才能提高程序的执行速度。 下面实例演示了 一个执行速度慢的程序。 该程序调用 3 次 func()函数, 但是该函数每次返回的值都是一样的。 (1) 在 vi 编辑器中编辑该程序。 程序清单 3-6 slow.c 一个多次调用函数, 执行速度慢的程序 01 #include <stdio. h> 02 int func(int a) 03 { 04 return a + 1; /* 返回参数加 1 */ 05 } 06 int main(void) 07 { 08 int res; 09 res = func(1) + func(1) + func(1) ; /*多次调用函数, 计算 3 次函数调用的 结果*/ 10 printf("the result : %d\n", res) ; /*输出变量*/ 11 return 0; } (2) 在 shell 中编译该程序。 $gcc slow. c -o slow (3) 在 shell 中运行该程序。 $. /slow the result : 6 该程序调用 3 次 func()函数, 每次该函数的返回值都是 2, 函数 func()的调用栈帧如 第 3 章 C 语言中的函数 · 53·图 3-5 所示。 图 3-5 3 次调用 func()函数的栈帧 说明: 由此可知, 该程序调用 3 次 func()函数完全没有必要, 由于 func()函数每次计算后的返回值都相同, 因此只需要调用 1 次即可。 下面是一个改进版的函数调用程序。 该程序只调用 1 次 func()函数之后, 将 func()函数的返回值保存起来, 用于后面的计算。 (1) 在 vi 编辑器中编辑该程序。 程序清单 3-7 fast.c 执行速度快的函数调用版本 01 #include <stdio. h> 02 int func(int a) 03 { 04 return a + 1; /* 返回参数加 1 */ 05 } 06 int main(void) 07 { 08 int res; 09 res = 3 * func(1) ; /* 只调用一次 func() 函数, 并使用函数的返回值进行乘法 运算 */ 10 printf("the result : %d\n", res) ; /* 输出结果 */ 11 return 0; 12 } (2) 在 shell 中编译该程序。 $gcc fast. c -o fast (3) 在 shell 中运行该程序。 $. / fast the result : 6 可以看出, 改进版的函数调用程序执行速度更快, 设计思想也更合理。 将多个运行结果相同的函数合并是一种合理的优化方法, 但是这种优化方法的基础在于运行结果相同,而并不是简单的返回值相同。 注意: 有时即使每次调用 函数的返回值相同, 但这种优化方法也是不能成立的。 第 1 篇 Linux 下 C 语言基础 · 54· 下面实例演示了 这种优化方面的错误。 (1) 在 vi 编辑器中编辑该程序。 程序清单 3-8 optim.c 合并多次返回值相同的函数调用出错 01 #include <stdio. h> 02 int count = 0; /* 全局变量 */ 03 int func(int a) 04 { 05 count++; 06 return a + 1; /* func 函数内部操作 */ /* 返回参数加 1 */ 07 } 08 int main(void) 09 { 10 int res; 11 res = 3 * func(1) ; /* 只调用一次 func() 函数, 并使用函数的返回值进行乘法 运算 */ 12 printf("the count : %d\n", count) ; /* 输出全局变量的值 */ 13 printf("the result : %d\n", res) ; /* 输出函数返回值的和 */ 14 return 0; 15 } (2) 在 shell 中编译该程序。 $gcc optim. c -o optim (3) 在 shell 中运行该程序。 $. /optim the count : 1 the result : 6 由于 func()函数只调用了 1 次, 所以全局变量 count 只累加了 1 次, 其值为 1。 如果不做优化, 将“3 * func(1);” 展开为“func(1) + func(1) + func(1)” , 这时 count 的值应当是 3,而不是 1。 因此合并多个函数的前提条件是每个函数的运行结果一样, 而不是简单的返回值一样。 3.4.2 变量存储优化 C 语言程序中的局部变量存储在栈上, 而全局变量存储在数据段上。 由于二者存储位置的不同导致了 二者生命期的不同。 注意: 全局变量的生命期是整个程序, 而局部变量的生命期仅在定义局部变量的函数结束调用 后就结束了 。 对于在程序中调用频率很高的局部变量, 编译器会自动将其存储在寄存器中, 但是全局变量不会存储在寄存器中。 因为全局变量需要在整个程序运行的过程中一直存在, 如果全局变量存储在寄存器中的话, 那么在整个程序的执行过程中, 该寄存器都将无法存储临 第 3 章 C 语言中的函数 · 55·时变量或者中间值了。 这对于寄存器资源稀少的计算机来说是不能接受的, 程序的运行效率也会大打折扣。 因此全局变量一定存储在数据段上, 也就是存储在内存中。 这一点很重要, 因为计算过程中访问内存的时间要远超过 CPU 的计算时间。所以尽量减少内存的访问是一个提高程序执行速度的好方法。 下面演示了 一个这样的实例, 对比程序清单 3-9 和程序清单 3-10,比较一下这两个代码片段的执行速度。 下面程序使用一个全局变量作为循环算子, 在函数f()内部执行一个循环。 该循环共循环 100 次, 每次循环需要访问全局变量 i。 程序清单 3-9 global.c 使用全局变量作为循环算子 01 int i; /* 全局变量作为循环因子 */ 02 void f() 03 { 04 int a[10] ; 05 for(i = 0; i < 100; i++ ) /* 将数值赋值到数组中 */ 06 a[i] = i; 07 } 下面程序使用一个局部变量作为循环算子, 在函数 f()内部执行一个循环。 该循环同样循环 100 次, 每次访问局部变量 i。 程序清单 3-10 local.c 使用局部变量作为循环算子 01 void f() 02 { 03 int i; /* 局部变量作为循环因子 */ 04 05 for(i = 0; i < 100; i++ ) a[i] = i; /* 将数值赋值到数组中 */ 06 } 程序清单 3-9 的代码中循环算子 i 是一个全局变量, 全局变量存储在内存上。 因此每次访问该全局变量时需要访问内存数据, 如图 3-6 所示。 CPU 内存 CPU CPU CPU(将 i 的值递增) 内存 内存 内存 将 i 的值回写取 i 的值 确定 i 的地址 图 3-6 访问一个作为循环因子的全局变量 在程序清单 3-10 的代码中循环算子 i 是一个局部变量, 对于这种高频率使用的局部变 第 1 篇 Linux 下 C 语言基础 · 56· 量编译器会自动将其存储在寄存器中。 因此每次访问这个局部变量时实际上是在做一个寄存器访问。 对比两个代码的执行流程, 不难发现程序清单 3-9 中每次循环需要做以下 3 步操作。 (1) 从内存单元中读取变量 i 的值。 (2) 对变量 i 进行累加。 (3) 将累加后的 i 的值存储到内存中。 而在程序清单 3-10 中每次循环只需要 1 步, 那就是累加变量 i 所在的寄存器的值。 由此可知, 程序清单 3-9 的执行效率远不及程序清单 3-10 的执行效率。 因此在编程过程中,不要将循环算子等高频率使用的变量设置为全局变量或者静态变量。 3.5 编写多 文件程序——变量的存储类别 当一个程序很大时不能将所有的代码都书写在一个源文件中, 这样做会严重破坏程序的模块化, 使程序变得难以维护。 因此这个时候需要将源程序代码书写在多个源文件中。 3.5.1 存储类别  auto: 自动变量, 默认的存储类别, 根据变量定义的位置决定变量的生命期和作用域。 如果变量定义在任何一个函数的外面, 该变量就是一个全局变量。 如果定义在函数的内部, 则该变量是局部变量。 在 C 语言中, 如果忽略变量的存储类别,编译器会认为该变量的存储类别为 auto。 这时编译器会自动为用户决定该变量应当存储的位置和性质。  register: 寄存器变量, 此类别的变量会被优先分配寄存器。 通常作为循环因子的变量会被分配寄存器。  extern: 外部变量(全局变量) , 该关键字用来扩展全局变量的作用域, 扩展的范围是从使用 extern 变量出现开始到该文件结束。 由于全局变量不像局部变量那样会因为栈帧的消失而消失。 所以 extern 关键字所做的工作只是让其他文件中的程序可以引用该变量, 并不改变该变量的生命期。  static: 静态变量, 用于限制作用域, 这种变量存储在数据段上, 无论该变量是全局变量还是局部变量。 静态全局变量的作用域仅限于该文件, 而静态局部变量的作用域仅在其定义所在的复合语句内。 对于静态局部变量而言...

关注我们

关注微信公众号

您选择了以下内容