嵌入式Linux与物联网软件开发 C语言内核深度解析

嵌入式Linux与物联网软件开发 C语言内核深度解析

参与本书整理和编写的学生

1 C语言与内存

1.1 引言

1.2 计算机程序运行的目的

1.2.1 什么是程序
1.2.2 计算机运行程序的目的
1.2.3 静态内存SRAM和动态内存DRAM
1.2.4 冯·诺伊曼结构和哈佛结构
1.2.5 总结:程序运行为什么需要内存呢
1.2.6 深入思考:如何管理内存(无OS时,有OS时)

1.3 位、字节、半字、字的概念和内存位宽

1.3.1 深入了解内存(硬件和逻辑两个角度)
1.3.2 内存的逻辑抽象图(内存的编程模型)
1.3.3 位和字节
1.3.4 字和半字
1.3.5 内存位宽(硬件和逻辑两个角度)

1.4 内存编址和寻址、内存对齐

1.4.1 内存编址方法
1.4.2 关键:内存编址是以字节为单位
1.4.3 内存和数据类型的关系
1.4.4 内存对齐

1.5 C语言如何操作内存

1.5.1 C语言对内存地址的封装
1.5.2 用指针来间接访问内存
1.5.3 指针类型的含义
1.5.4 用数组来管理内存

1.6 内存管理之结构体

1.6.1 数据结构这门学问的意义
1.6.2 最简单的数据结构:数组
1.6.3 数组的优缺点
1.6.4 结构体隆重登场
1.6.5 题外话:结构体内嵌指针实现面向对象

1.7 内存管理之栈(stack)

1.7.1 什么是栈
1.7.2 栈管理内存的特点(小内存、自动化)
1.7.3 栈的应用举例:局部变量和函数调用
1.7.4 栈的约束(预定栈大小不灵活,怕溢出)

1.8 内存管理之堆

1.8.1 什么是堆
1.8.2 堆管理内存的特点(大块内存、手工分配/使用/释放)
1.8.3 C语言操作堆内存的接口(malloc/free)
1.8.4 堆的优势和劣势(管理大块内存、灵活、容易内存泄漏)
1.8.5 静态存储区

课后题

2 C语言位操作

2.1 引言

2.2 常用位操作符

2.2.1 位与(&)
2.2.2 位或(|)
2.2.3 位取反(~)
2.2.4 位异或(^)
2.2.5 左移位(<<)
2.2.6 右移位(>>)

2.3 位操作与寄存器

2.3.1 寄存器的操作
2.3.2 寄存器特定位清零用&
2.3.3 寄存器特定位置1用|
2.3.4 寄存器特定位取反用~

2.4 位运算构建特定二进制数

2.4.1 使用移位获取特定位为1的二进制数
2.4.2 结合位取反获取特定位为0的二进制数
2.4.3 总结

2.5 位运算实战演练1

2.5.1 给定整型数a,设置a的bit3,保证其他位不变
2.5.2 给定整型数a,设置a的bit3~bit7,保持其他位不变
2.5.3 给定整型数a,清除a的bit15,保证其他位不变
2.5.4 给定整型数a,清除a的bit15~bit23,保持其他位不变
2.5.5 给定整型数a,取出a的bit3~bit8
2.5.6 用C语言给寄存器a的bit7~bit17赋值937(其余位不受影响)

2.6 位运算实战演练2

2.6.1 用C语言将寄存器a的bit7~bit17中的值加17(其余位不受影响)
2.6.2 用C语言给寄存器a的bit7bit17赋值937,同时给bit21bit25赋值17

2.7 技术升级:用宏定义来完成位运算

2.7.1 直接用宏来置位
2.7.2 直接用宏来复位
2.7.3 截取变量的部分连续位

课后题

3 指针才是C语言的精髓

3.1 引言

3.2 指针到底是什么

3.2.1 普通变量
3.2.2 指针变量
3.2.3 变量空间的首字节地址,作为整个空间的地址
3.2.4 指针变量的类型作用
3.2.5 为什么需要指针
3.2.6 高级语言如Java、C 的指针到哪里去了
3.2.7 指针使用之三部曲

3.3 理解指针符号

3.3.1 星号*的理解
3.3.2 取地址符&的理解
3.3.3 指针变量的初始化和指针变量赋值之间的区别
3.3.4 左值与右值
3.3.5 定义指针后,需要关心的一些内容

3.4 野指针与段错误问题

3.4.1 什么是野指针
3.4.2 野指针可能引发的危害
3.4.3 野指针产生的原因
3.4.4 如何避免野指针
3.4.5 NULL到底是什么
3.4.6 段错误产生的原因汇总

3.5 const关键字与指针

3.5.1 什么是const
3.5.2 const对于普通变量的修饰
3.5.3 const修饰指针的三种形式
3.5.4 const的变量真的不能改吗
3.5.5 为什么要用const
3.5.6 有关变量和常量的探讨

3.6 深入学习数组

3.6.1 为什么需要数组
3.6.2 从编译器角度理解数组
3.6.3 从内存角度理解数组
3.6.4 一维数组中几个关键符号的理解

3.7 指针与数组的天生“姻缘”

3.7.1 如何使用指针访问数组
3.7.2 从内存角度理解指针访问数组的实质
3.7.3 指针与数组类型的匹配问题
3.7.4 总结:指针类型决定了指针如何参与运算

3.8 指针类型与强制类型转换

3.8.1 变量数据类型的作用
3.8.2 数据的存入和读取
3.8.3 普通变量的强制转换
3.8.4 指针变量数据类型的含义
3.8.5 指针变量数据类型的强制转换

3.9 指针、数组与sizeof运算符

3.9.1 char str[]="hello"; sizeof(str) ,sizeof(str[0]) ,strlen(str)
3.9.2 char str[]="hello"; char p=str; sizeof(p)
3.9.3 int b[100]; sizeof(b)
3.9.4 数组的传参
3.9.5 define和typedef的区别

3.10 指针与函数传参

3.10.1 普通传参
3.10.2 传递地址(指针)
3.10.3 传递数组
3.10.4 传递结构体
3.10.5 传递普通值和传递地址的异同,以及传递地址(指针)应该遵循的原则

3.11 输入型参数与输出型参数

3.11.1 函数为什么需要传参和返回值
3.11.2 函数传参中为什么使用const指针
3.11.3 总结

课后题

4 C语言复杂表达式与指针高级应用

4.1 引言

4.2 指针数组与数组指针

4.2.1 简单理解指针数组与数组指针
4.2.2 分析指针数组与数组指针的表达式

4.3 函数指针与typedef

4.3.1 函数指针的实质(还是指针变量)
4.3.2 函数指针的书写和分析方法
4.3.3 typedef关键字的用法

4.4 函数指针实战1——用函数指针调用执行函数

4.5 函数指针实战2——结构体内嵌函数指针实现分层

4.6 再论typedef

4.6.1 轻松理解和应用typedef
4.6.2 typedef与 define宏的区别
4.6.3 typedef与struct
4.6.4 typedef与const
4.6.5 使用typedef的重要意义
4.6.6 二重指针

4.7 二维数组

4.7.1 二维数组的内存映像
4.7.2 识别第一维和第二维
4.7.3 数组名代表数组首元素的地址
4.7.4 指针访问二维数组的两种方式
4.7.5 总结

课后题

5 数组&字符串&结构体&共用体&枚举

5.1 引言

5.2 程序中的内存从哪里来

5.2.1 管理方式:栈(stack)、堆(heap)、数据区(.data)
5.2.2 栈内存特点详解

5.3 堆

5.3.1 堆内存特点详解
5.3.2 使用堆内存注意事项
5.3.3 malloc的一些细节表现

5.4 内存中的各个段

5.4.1 代码段、数据段、bss段
5.4.2 特殊数据会被放到代码段
5.4.3 未初始化或显式初始化为0的全局变量放在bss段
5.4.4 内存管理方式的总结

5.5 C语言的字符串类型

5.5.1 C语言使用指针来管理字符串
5.5.2 C语言中字符串的本质:指向字符串的存放空间的指针
5.5.3 指向字符串的指针变量空间和字符串存放的空间是分开的
5.5.4 存储多个字符的两种方式——字符串和字符数组

5.6 字符串和字符数组的细节

5.6.1 字符数组的初始化、sizeof以及strlen
5.6.2 字符串的初始化与sizeof、strlen
5.6.3 字符数组与字符串的本质差异

5.7 结构体概述

5.7.1 结构体使用时先定义结构体类型,再用类型定义变量
5.7.2 从数组到结构体的进步之处
5.7.3 结构体变量中的元素如何访问

5.8 结构体的对齐访问

5.8.1 结构体对齐访问实例
5.8.2 结构体为何要对齐访问
5.8.3 结构体对齐的规则和运算
5.8.4 手动对齐
5.8.5 GCC推荐的对齐指令:attribute((packed))和_attribute_((aligned(n)))

5.9 offsetof宏与container_of宏

5.9.1 通过结构体指针访问各结构体成员的原理
5.9.2 offsetof宏
5.9.3 container_of宏
5.9.4 学习指南和要求

5.10 共用体(union)

5.10.1 共用体的类型声明、变量定义和使用
5.10.2 共用体和结构体的区别
5.10.3 共用体的主要用途

5.11 大小端模式

5.11.1 什么是大小端模式
5.11.2 用union来测试机器的大小端模式
5.11.3 用指针方式来测试机器的大小端
5.11.4 通信系统中的大小端(数组的大小端)

5.12 枚举enum

5.12.1 枚举的作用是什么
5.12.2 C语言为何需要枚举
5.12.3 宏定义和枚举的区别
5.12.4 枚举的定义形式

课后题

6 C语言的预处理、函数和函数库

6.1 引言

6.2 C语言为什么需要编译链接

6.2.1 编译链接的流程
6.2.2 编译链接中各种文件扩展名的含义

6.3 预处理详解

6.3.1 C语言预处理的意义
6.3.2 预处理涉及的内容
6.3.3 使用GCC进行编译和链接的过程

6.4 常见的预处理详解

6.4.1 文件包含
6.4.2 注释
6.4.3 宏定义
6.4.4 条件编译

6.5 函数的本质

6.5.1 C语言为什么会有函数
6.5.2 函数书写的一般原则
6.5.3 函数是动词、变量是名词(面向对象语言中分别叫方法和成员变量)
6.5.4 函数的实质是数据处理器

6.6 函数的基本使用

6.6.1 函数三要素:定义、声明、调用
6.6.2 函数原型和作用

6.7 递归函数

6.7.1 函数的调用机制
6.7.2 递归函数
6.7.3 使用递归的原则:收敛性、栈溢出
6.7.4 递归与循环的区别

6.8 库函数

6.8.1 什么是函数库
6.8.2 函数库的由来
6.8.3 函数库的提供形式:静态链接库与动态链接库
6.8.4 库函数的使用

6.9 常见的库函数之字符串函数

6.9.1 什么是字符串
6.9.2 字符串处理函数
6.9.3 man手册的引入
6.9.4 man手册的使用
6.9.5 常用的字符串处理函数

6.10 常见的库函数之数学库函数

6.10.1 数学库函数
6.10.2 计算开平方
6.10.3 链接时加-lm

6.11 制作静态链接库并使用

6.12 制作动态链接库并使用

课后题

7 存储类&作用域&生命周期&链接属性

7.1 引言

7.2 概念解析

7.2.1 存储类
7.2.2 作用域
7.2.3 生命周期
7.2.4 链接属性

7.3 Linux下C程序的内存映像

7.3.1 代码段、rodata段(只读数据段)
7.3.2 数据段、bss段
7.3.3 堆
7.3.4 文件映射区
7.3.5 栈
7.3.6 内核映射区
7.3.7 操作系统下和裸机下C程序加载执行的差异

7.4 存储类相关的关键字1

7.4.1 auto
7.4.2 static
7.4.3 register

7.5 存储类相关的关键字2

7.5.1 extern
7.5.2 volatile
7.5.3 restrict
7.5.4 typedef

7.6 作用域详解

7.6.1 局部变量的代码块作用域
7.6.2 函数名和全局变量的文件作用域

7.7 变量的生命周期

7.7.1 研究变量生命周期的意义
7.7.2 栈变量的生命周期
7.7.3 堆变量的生命周期
7.7.4 数据段、bss段变量的生命周期
7.7.5 代码段、只读段的生命周期

7.8 链接属性

7.8.1 C语言程序的组织架构:多个C文件+多个h文件
7.8.2 编译以文件为单位、链接以工程为单位
7.8.3 三种链接属性:外连接、内链接、无链接
7.8.4 函数和全局变量的命名冲突问题
7.8.5 static的第二种用法:修饰全局变量和函数

课后题

8 C语言关键细节讨论

8.1 引言

8.2 操作系统概述

8.2.1 什么是操作系统
8.2.2 C库函数
8.2.3 操作系统的重大意义

8.3 main函数返回值

8.3.1 普通函数的返回值
8.3.2 main函数的返回值
8.3.3 谁调用了main函数

8.4 argc、argv与main函数的传参

8.5 void类型的本质

8.6 C语言中的NULL

8.6.1 NULL的定义
8.6.2 '\0'、'0'、0 和NULL的区别

8.7 运算中的临时匿名变量

8.7.1 C语言和汇编语言的区别
8.7.2 强制类型转换
8.7.3 使用临时变量来理解不同数据类型之间的运算

8.8 顺序结构

8.8.1 C语言中的结构
8.8.2 编译过程中的顺序结构
8.8.3 思考:为什么本质都是顺序结构

8.9 程序调试

8.9.1 程序调试手段
8.9.2 调试(DEBUG)版本和发行(RELEASE)版本的区别
8.9.3 debug宏的使用方法

课后题

9 链表&状态机&多线程

9.1 引言

9.2 链表的引入

9.2.1 数组的缺陷
9.2.2 感性地认识链表
9.2.3 链表的作用是什么

9.3 单链表的实现之构建一个点

9.3.1 单向链表
9.3.2 单向链表的结构
9.3.3 单链表的节点构成
9.3.4 使用堆内存创建一个节点
9.3.5 链表的头指针
9.3.6 构建第一个简单的链表

9.4 单链表的实现之从尾部插入节点

9.4.1 从尾部插入节点
9.4.2 构建第一个简单的链表
9.4.3 什么是头节点

9.5 单链表的算法之从头部插入节点

9.5.1 链表头部插入思路解析
9.5.2 箭头非指向

9.6 单链表的算法之遍历节点

9.6.1 什么是遍历
9.6.2 如何遍历单链表
9.6.3 代码分析

9.7 单链表的算法之删除节点

9.7.1 为什么要删除节点
9.7.2 注意堆内存的释放
9.7.3 设计一个删除节点算法

9.8 单链表的算法之逆序

9.8.1 什么是链表的逆序
9.8.2 单链表的逆序算法分析
9.8.3 编程实现逆序算法
9.8.4 数据结构与算法的关系

9.9 双链表的引入和基本实现

9.9.1 单链表的优缺点
9.9.2 双链表的结构

9.10 双链表的算法之插入节点

9.10.1 尾部插入
9.10.2 头部插入

9.11 双链表的算法之遍历

9.11.1 正向遍历
9.11.2 逆向遍历

9.12 双链表的算法之删除节点

9.13 Linux内核链表

9.13.1 前述链表数据区域的局限性
9.13.2 解决思路:数据区的结构体的封装由用户实现,通用部分通过调用函数实现
9.13.3 内核链表的设计思路
9.13.4 list.h文件简介

9.14 内核链表的基本算法和使用简介

9.14.1 内核链表的常用操作
9.14.2 内核链表的使用实践

9.15 什么是状态机

9.15.1 有限状态机
9.15.2 两种状态机:Moore型和Mealy型
9.15.3 状态机的主要用途
9.15.4 状态机解决了什么问题

9.16 用C语言实现简单的状态机

9.16.1 题目:开锁状态机
9.16.2 题目分析

9.17 多线程简介

9.17.1 操作系统下的并行执行机制
9.17.2 进程和线程的区别和联系
9.17.3 多线程的优势

课后题

10 程序员和编译器的暧昧

10.1 引言

10.2 编程工作的演进史

10.2.1 CPU与二进制
10.2.2 编程语言的革命

10.3 程序员、编译器和CPU之间的三角恋

10.3.1 程序员与CPU的之间的“翻译”—编译器
10.3.2 高级语言与低级语言的差别

10.4 像编译器一样思考吧——理论篇

10.4.1 编译器的结构
10.4.2 语法是什么?语法就是编译器的习性

10.5 像编译器一样思考吧——实战篇

10.5.1 充分地利用语法规则,写出简洁、高效的代码
10.5.2 复杂表达式理解

课后题

附录 答案

1 课后题答案

2 课后题答案

3 课后题答案

4 课后题答案

5 课后题答案

6 课后题答案

7 课后题答案

8 课后题答案

9 课后题答案

10 课后题答案

思维导图

嵌入式Linux与物联网软件开发 C语言内核深度解析

防止博客图床图片失效,防止图片源站外链:

http://www.processon.com/chart_image/5e5b2c22e4b02bc3ad6a0e05.png)

思维导图在线编辑链接:

https://www.processon.com/view/5e5b2c22e4b02bc3ad6a0e02

posted @ 2020-04-29 12:31  哀莫  阅读(854)  评论(0编辑  收藏  举报

欢迎访问我的主页!