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”!

数据类型对齐
char1
short2
int4
其它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)
    – 时间片轮询
  • 数据在函数间的传递模式
    – 全局变量
    – 函数入参与返回值
    – 利用“内存结构”
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值