堆
堆就是一段能动态分配的内存。用malloc()分配。
栈
栈就是一种“后进先出”的数据结构。
栈顶:允许插入和删除元素的一端,最后放入的元素在栈顶。
栈底:栈的另一端,最早放入的元素在栈底,不能直接操作。
栈深度:栈能容纳的最大元素数量。
压栈:把新元素放到栈顶。
弹栈:把栈顶元素取出并删除。
先进后出:先压入栈的元素会被后压入的元素压在下面,只能等上面的元素都弹出后才能出来,所以“先进后出”。
调用栈
“调用栈”就是由系统自动维护的栈,作用是在函数调用发生时保存上下文。
函数调用之前需要保存上下文,包括:参数、局部变量和返回地址。
| //main方法 int main { int ret; ret = f(3); return ret; } | //f() = 10x +3 + g(y) int f(int x) { int a1,a2,a3,ret; a1 = 10 * x; a2 = 3; a3 = g(x); ret = a1 + a2 + a3; return ret; } | //g() = y^2 +5y int g(int y) { int b1,b2,ret; b1 = square(y); b2 = 5 * y; ret = b1 + b2; return ret; } | //计算平方 int square(int z) { return z*z; } |
|---|---|---|---|
| main()方法的上下文 参数:无 局部变量:ret 返回地址:main()+3 | f()方法的上下文 参数:x 局部变量:a1,a2,a3,ret 返回地址:f()+5 | g()方法的上下文 参数:y 局部变量:b1.b2,ret 返回地址:g()+3 |
在FreeRTOS中,为每个任务分配一个调用栈,通过heap_x动态分配。
示例:估算一下任务的栈深度。
1234567891011121314151617181920212223 | static void f(void){ float b[64];//局部变量,浮点型数组,长度64 ......} void vTask1(void *pvParameters){ int a[64];//局部变量,整形数组,长度64 f(); ......} int main(void){ ...... xTaskCreate(vTask1, "task1", 栈深度, NULL, 1, NULL); vTaskStartScheduler(); while(1) { ...... }} |
| vTask1() | f() | |
|---|---|---|
| 参数 | void *pvParameters(4个字节) | 无 |
| 局部变量 | int a[64](256个字节) | float b[64](256个字节) |
| 返回地址 | 4个字节 | 无 |
栈深度,单位是 字(word)。在常见的 32 位单片机(如 STM32、ESP32)上,StackType_t 通常是 uint32_t,即 4 字节,所以分配的字节数应该大于:520字节/4 = 130字
因此栈深度要写一个大于130的数字,比如设置成256。