目录

1.  堆和栈

1.1  堆

1.2  栈

1.3  堆和栈的区别

1.3.1  分配方式

1.3.2  分配效率

1.3.3  生长方向

1.3.4  空间管理

1.3.5  存放内容


1.  堆和栈

1.1  堆

        堆是一块用于动态分配内存的区域,用于存储程序运行时动态创建的对象。堆的大小可以在程序运行时动态调整。

        堆由开发人员分配和释放, 若开发人员不释放,程序结束时由 OS 回收。

        堆的分配和释放需要调用相应的函数,如malloc()和free函数。

        在FreeRTOS中堆用来分配任务、队列、信号量、互斥量等内核对象的内存。

        FreeRTOS 提供了 5 种堆管理实现(heap_1 到 heap_5),开发者可通过 FreeRTOS/Source/portable/MemMang 目录下的文件选择。

        堆大小:在 FreeRTOSConfig.h 中通过 configTOTAL_HEAP_SIZE 定义(单位:字节)。 

void *pvPortMalloc(size_t xSize);  // 分配内存
void vPortFree(void *pv);          // 释放内存

        堆的内存地址生长方向与栈相反,由低到高,但需要注意的是,后申请的内存空间并不一定在先申请的内存空间的后面,即 p2 指向的地址并不一定大于 p1 所指向的内存地址,原因是先申请的内存空间一旦被释放,后申请的内存空间则会利用先前被释放的内存,从而导致先后分配的内存空间在地址上不存在先后关系。堆中存储的数据若未释放,则其生命周期等同于程序的生命周期。

int main() {
	// C 中用 malloc() 函数申请
	char* p1 = (char *)malloc(10);
	cout<<(int*)p1<<endl;		//输出:00000000003BA0C0
	
	// 用 free() 函数释放
	free(p1);
   
	// C++ 中用 new 运算符申请
	char* p2 = new char[10];
	cout << (int*)p2 << endl;		//输出:00000000003BA0C0
	
	// 用 delete 运算符释放
	delete[] p2;
}

1.2  栈

        栈是一种用于存储局部变量和函数调用信息的数据结构。栈的大小在程序编译时确定,通常较小。

        栈由操作系统自动分配释放 ,用于存放函数的参数值、局部变量等,其操作方式类似于数据结构中的栈。参考如下代码:

int main() {
	int b;				//栈
	char s[] = "abc"; 	//栈
	char *p2;			//栈
}

        栈的内存地址生长方向与堆相反,由高到底,所以后定义的变量地址低于先定义的变量,比如上面代码中变量 s 的地址小于变量 b 的地址,p2 地址小于 s 的地址。

        栈中存储的数据的生命周期随着函数的执行完成而结束。

        在FreeRTOS中,栈大小:在创建任务时通过 usStackDepth 参数指定(单位:sizeof(StackType_t))。

xTaskCreate(TaskFunction, "Task", usStackDepth, NULL, priority, &Handle);

         栈的分配方式有两种类型:

  • 静态栈:预先分配固定大小的数组(StaticTask_t)。
  • 动态栈:从堆中动态分配内存(默认方式)。

FreeRTOS菜鸟入门(七)·创建任务·静态任务创建-CSDN博客

FreeRTOS菜鸟入门(八)·创建任务·动态任务创建_freertos 创建动态任务的堆栈-CSDN博客

1.3  堆和栈的区别

1.3.1  分配方式

堆都是动态分配的,由程序员手动控制,堆的分配可以在任意时刻进行,不需要遵循特定的顺序。

栈有 2 种分配方式:静态分配和动态分配。

        静态分配是由操作系统完成的,由编译器自动控制,栈的分配在编译时确定,无法在运行时改变,比如局部变量的分配。

        动态分配由alloca()函数分配,但是栈的动态分配和堆是不同的,它的动态分配是由操作系统进行释放,无需我们手工实现。

        栈还用于中断时的保存现场和恢复现场。

简单来说,

堆的分配和释放由程序员控制,分配后可变;

栈的分配和释放由编译器控制,分配后不可变。

1.3.2  分配效率

        堆则是由C/C++提供的库函数或运算符来完成申请与管理,实现机制较为复杂,频繁的内存申请容易产生内存碎片。

        栈由操作系统自动分配,会在硬件层级对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。

堆的分配效率较低,需要在运行时动态分配和释放。

栈的分配效率较高,由编译器在编译时确定分配大小。

1.3.3  生长方向

堆的生长方向向上,内存地址由低到高;

对于堆需要注意:后申请的内存空间并不一定在先申请的内存空间的后面,原因是先申请的内存空间一旦被释放,后申请的内存空间则会利用先前被释放的内存,从而导致先后分配的内存空间在地址上不存在先后关系。

栈的生长方向向下,内存地址由高到低。

1.3.4  空间管理

堆的空间管理由程序员手动控制,需要注意内存泄漏和内存碎片的问题。

栈的空间管理由编译器自动控制,无效程序员关注。

1.3.5  存放内容

堆:

任务控制块(TCB):TaskHandle_t 指向的结构体,包含任务状态、优先级、栈指针等信息。

任务栈空间(动态分配时):通过 xTaskCreate() 创建任务时,从堆中分配栈内存。

队列(Queue):xQueueCreate() 创建的队列数据结构和存储区域。

信号量/互斥量:xSemaphoreCreateBinary()、xSemaphoreCreateMutex() 等创建的同步对象。

定时器(Timer):xTimerCreate() 创建的软件定时器结构。

动态分配的缓冲区:用户通过 pvPortMalloc() 手动申请的内存块。

栈:

局部变量:函数内定义的变量(如 int temp)。

函数调用链:返回地址、函数参数、寄存器保存值(中断或任务切换时)。

中断现场:发生中断时,CPU 自动压栈的寄存器状态(如 R0-R12, LR, PC)。

表达式计算:算术运算的中间结果。

拓展:

嵌入式面试八股文(十一)·FreeRTOS相关题目_freertos习题-CSDN博客

千题千解·嵌入式工程师八股文详解_时光の尘的博客-CSDN博客

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐