2021/10/27 paradigm 笔记
1、C++函数重载原理
C++,调用函数时,根据不同的label来实现重载!
例如:
int memcmp(void *p)
{
int n = 17;
int m = memcmp(&n)
}
如果是C,翻译成汇编语言:
CALL <memcmp>
如果是C++,翻译成汇编语言:
CALL <memcmp_void_p>
再如:
int memcmp(void *v1, void *v2, int size)
如果是C,翻译成汇编语言:
CALL <memcmp>
如果是C++,翻译成汇编语言:
CALL <memcmp_void_p_void_p_int>
对于C++,入参的不一致 → “链接”错误→相比于C更安全,但也更死板!
2、debug error
-
segment fault 段错误
使用内存没有映射到“段”的地址导致的!在我们的“抽象内存模型”中,内存被分为“stack”、“heap”和“code”三段!NULL = 0,不在任何一个段内,如果对 NULL 进行 dereference,就会报 “segment fault”! -
bus error 总线错误
硬件和操作系统对所有数据类型变量的地址进行了限制,int型变量的地址应该是4的倍数!使用指针时候,如果地址没对齐,会报“bus error”!
数据类型 | 对齐 |
---|---|
char | 1 |
short | 2 |
int | 4 |
其它 | 4 |
void *vp =...
*(short *)vp = 7; // 50% → bus error
*(int *)vp = 7; // 75% → bus error
*(char *)vp = 7; // OK!!!
3、缓冲区溢出
缓冲区溢出(buffer overflow)是指下面这种情况:
int main()
{
int i;
int array[4];
for(i = 0; i < 4; i++) {
array[i] = 0;
};
return 0;
}
4、缓冲区溢出之大小尾不同
int main()
{
int i;
short array[4];
for(i = 0; i <= 4; i++) {
array[i] = 0;
};
return 0;
}
5、更有趣的“无限递归”错误
void foo()
{
int array[4];
int i;
for(i = 0; i <= 4; i++) {
array[i] -= 4;
}
}
6、利用“内存结构”传递数据
void DeclearAndInitArray()
{
int array[100];
int i;
for(i = 0; i < 100; i++) {
array[i] = i;
}
}
void PrintArray()
{
int array[100];
int i;
for(i = 0; i < 100; i++) {
printf("%d\r\n", array[i]);
}
}
int main(int argc, char **argv)
{
DeclearAndInitArray();
PrintArray();
}
为什么没有进行函数之间的数据传递,仍然能够正确打印出来 0~99呢?
答:调用DeclearAndInitArray(),活动记录中有408(4+100x4+1x4)个字节,数组占据了saved PC下面的400个字节,DeclearAndInitArray()函数RET后,虽然这存放数组的400个字节已经出栈,但是并没有被清0;
继续调用PrintArray(),它的活动记录将占用刚才DeclearAndInitArray()的位置,并且活动记录的结构与DeclearAndInitArray()一模一样,此时,直接打印数组中的数据,会成功打印出DeclearAndInitArray()留在内存中的0~99。
这个特性能用在哪里?答:中断服务函数中!
留给中断服务函数处理事务的时间是很短的,我们可以先构造好一些常量数据,存放在“栈”中某个地方,这样在中断服务函数中就免于构造这些数据的麻烦了!
7、为什么入参要从右向左压栈?
为了实现“可变参数”…
通常有“可变参数”的编程语言,函数入参都是从右向左压栈!
int printf(const char *control, ...);
…是可变参数!可以是任何类型数据,并且“个数”不限!
printf("%d + %d = %d \r\n", 4, 4, 8);
8、为什么复合数据类型数据要从先到后压栈?
struct type {
int code;
};
struct type_one {
int code;
...
};
从上到下压栈,可以得到code与结构体变量基地址的距离!反着来,得到这个“距离”就依赖于总长度了,会变得复杂!!!
比如咱们的网络IP地址,以前的网络地址是IPv4的,用4个字节表示IP地址,某天大家意识到,4个字节的IP地址会很快被用光,因此开发了6个字节的IP地址。IPv4和IPv6对应着不同的结构体,我们希望IPv6能够兼容IPv4,所以IPv6的前4个字节结构与IPv4是一样的!要是IPv6的前4个字节后入栈,很难得到其余结构体变量基地址的距离了,会给变成带来麻烦!
9、什么是虚拟地址空间?
10、模拟“单一航班出售150张票”
int main()
{
int numAgents = 10;
int numTickets = 150;
for(int agent = 1; agent <= numAgents; agent++) {
SellTickets(agent, numTickets / numAgets);
}
return 0;
}
void SellTickets(int agentID, int numTickestoSell)
{
while(numTickestoSell > 0) {
printf("Agent %d sells a ticket \r\n", agentID);
numTickestoSell--;
}
printf("Agent %d: All down.\r\n", agentID);
}
预期结果:
打印出来160行;其中 Agent %d: All down.\r\n 占用10行。
上面这样实现,每个agent会依次买票,并不会10个agents同时一起买票!
希望使用1个“线程”包。
int main()
{
int numAgents = 10;
int numTickets = 150;
InitThreadPackage(false);
for(int agent = 1; agent <= numAgents; agent++) {
char name[32];
sprintf(name, "Agent %s Thread", agent);
ThreadNew(name, SellTickets, 2, agent, numTickets / numAgets); // 把狗狗们都放到起跑线上
RunAllThreads(); // 把门打开,大家一起跑啊!
return 0;
}
void SellTickets(int agentID, int numTickestoSell)
{
while(numTickestoSell > 0) {
printf("Agent %d sells a ticket \r\n", agentID);
numTickestoSell--;
if(RandomChance(0.1)) {
// 模拟条件:10% 概率被强制暂停运行
ThreadSleep(1000);
}
}
printf("Agent %d: All down.\r\n", agentID);
}
paradigm 关键词总结:
适用于单片机程序设计的一套paradigm体系
Part 1 C基础(功能实现)
- C的数据类型及其位模式
– 原子类型:char、short、int、long、float、double、void *
– 复合类型:array、string、struct、union
– 特殊类型:bool、sfr、sfr16、sbit - 算法与数据结构
– 筑基_C_1 判断大小端模式
– 筑基_C_2 打印数据的位模式
– 筑基_C_3 交换两个变量的值
– 筑基_C_4 线性搜索
– 筑基_C_5 二分搜索
– 筑基_C_6 冒泡排序
– 筑基_C_7 选择排序
– 筑基_C_8 快速排序
– 筑基_C_9 rotate()
– 筑基_C_10 栈
– 筑基_C_11 队列
– 筑基_C_12 链表
Part 2 C的底层机制(性能优化)
- 筑基_C_13 内存模型抽象
– heap段:malloc()、realloc()与free()
– stack段:“活动记录”
– code段:“指令编码” - 筑基_C_14 微架构
– “ALU—SFR—Memory” 结构
– 总线
– “流水线” - 筑基_C_15 汇编指令集
– load_store类
– ALU类
– PC类 - 筑基_C_16 C语言调用函数的汇编实现
– main() 调用 foo()
– 递归
– C结构体和C++的类与this
– C++函数重载的实现
– 数据的压栈顺序 - 筑基_C_17 预处理、编译和链接
– 预处理:宏替换
– 编译:根据.h的函数原型进行语法检查
– 链接:去找.c→.o中的“函数实现”拼接在一起 - 筑基_C_18 debug error
– segment fault:使用了“段外地址”
– bus error:地址对齐
– debug宏函数
Part 3 程序架构模型(构建完整程序)
- super-loop + Interrupt
- 有限状态机
- 多线程
– 虚拟地址空间(MMU)
– 时间片轮询 - 数据在函数间的传递模式
– 全局变量
– 函数入参与返回值
– 利用“内存结构”