单片机中堆和栈的区别

M0中的栈和堆

一、栈和堆空间的区别:

1)栈区(stack):由编译器自动分配和释放,存放函数的参数值、局部变量的值等,其操作方式类似于数据结构中的栈。
2)堆区(heap):一般由程序员分配和释放,若程序员不释放,程序结束时可能由操作系统回收。分配方式类似于数据结构中的链表。
3)全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统自动释放。

4)文字常量区:常量字符串就是存放在这里的。

5)程序代码区:存放函数体的二进制代码

如图:在地址中栈和堆的地址及存储方式。

https://i-blog.csdnimg.cn/blog_migrate/57b6c457fc155a01da7ebded9f246984.png

图一

 

二、栈和堆空间的空间分配

栈:向低地址扩展(如图一)

堆:向高地址扩展(如图一)

显然如果依次定义变量,

先定义的栈变量的内存地址比后定义的栈变量的内存地址要大

先定义的堆变量的内存地址比后定义的堆变量的内存地址要小

三、堆和栈变量
栈:临时变量,退出该作用域就会自动释放

存函数的临时变量,即局部变量,函数返回时随时有可能被其他函数栈用。所以栈是一种分时轮流使用的存储区。编译器里定义的Stack_Size,是为了限定函数的局部数据活动的范围,超过这个范围就会跑飞,也就是栈溢出;
     Stack_Size不影响Hex,更不影响Hex怎么运行的,只是在Debug调试时会提示报错。栈溢出也就是超过了国界进行活动,只要老外没有意见,你可以接着玩,有老外不让你玩,你就的得死,或是大家都死(互相撕杀),有的人写单片机代码在函数里定义一个大数组 int buf[8192],栈要是小于8192是会死的很惨。 所以不要在局部变量里定义大数组。

堆:malloc变量申请空间,通过free函数释放(若是不释放一直占用空间,最后空间占满溢出,程序崩溃)

所以若是程序中有用到malloc要申请变量空间,这时就要在编译

器里定义堆的大小。先分配空间,后程序中才能申请,用完一定

要释放,不然就会造成内存溢出,程序崩溃。

总之:堆和栈都存在RAM里,他两各分多少看函数需求,但是他两的总值不能超过单片机硬件的实际RAM大小,否则只能外扩SRAM,或者程序就要崩溃死掉。

另外:堆栈溢出,编译不会提示,需要注意

总结:

如果我们设置了堆的空间大小,但是我们程序中没有进行malloc申请,那么程序在运行的时候,我们栈的空间超过本身设置的空间,进入到堆里面,那么程序是不会出错的,但是超过了堆的空间了,进入到全局变量区域,就会出现莫名其妙的错误。

 若是我们不使用malloc,我们可以将堆设置成0,这是没有问题的,但是栈的空间大小要设置成合适的,不然就会因为栈溢出,进入harderror,程序奔溃。

四、M0中栈和堆的分配。

图二

M0中栈和堆的分配是在开始文件里,这里已stm32F0系列为例,他是在startup_stm32f00xx.s文件里。(如图二)

Stack_Size      EQU     0x00000200

定义栈的大小,此时0x00000200定义栈为512bit

Heap_Size       EQU     0x00000100

定义堆的大小,此时0x00000100定义堆为256bit

若是想修改大小,在这里修改即可。

<think>嗯,用户问的是GD32F527系列单片机中的底的定义。首先,我需要回忆一下单片机中的结构。在嵌入式系统中通常用于存储局部变量、函数调用时的返回地址寄存器状态等。ARM Cortex-M系列的处理器,比如GD32F527可能采用的Cortex-M4或类似的内核,其操作是“满递减”类型的,也就是指针指向最后一个被使用的元素,并且向内存地址减小的方向增长。 接下来,底的定义可能需要结合启动文件链接脚本来分析。启动文件中通常会设置初始的顶地址,这个地址通常位于RAM的最高地址处,因为是向下增长的。底则可能是空间的最低地址,也就是顶减去的大小后的地址。但具体到GD32F527,我需要确认其内存布局。 还要考虑链接脚本中的分配,比如的大小在链接脚本中定义,通常命名为_STACK_SIZE,然后顶由链接器符号__StackTop或者类似名称表示,而底则是__StackTop - __STACK_SIZE。不过具体符号名称可能因厂商而异,比如GD32可能会用不同的命名方式。 另外,单片机启动时,顶的初始值会被加载到主指针(MSP)中,程序开始运行时使用这个指针。底的实际地址取决于顶初始值的大小。例如,如果顶初始地址是0x2000C000,大小是2KB,那么底就是0x2000C000 - 0x800 = 0x2000B800。 需要验证这些信息是否正确,可能需要参考GD32F527的参考手册或数据手册,确认其内存地址分配启动流程。此外,是否使用双(主进程)的情况在Cortex-M中通常主用于异常内核代码,而进程可能由OS管理,但如果没有OS,可能只使用主。 总结来说,顶由启动文件定义,位于RAM的高地址,底则是顶减去的大小后的地址,指针初始指向顶,随着数据入递减。需要确保这些概念在GD32F527中的具体实现是否如此,可能需要查看官方文档或示例代码中的启动文件链接脚本。</think>在GD32F527系列单片机(基于ARM Cortex-M系列内核)中,的定义与ARM架构特性密切相关。以下是逐步说明: --- ### **1. 的基本作用** 用于存储: - 函数调用时的返回地址 - 局部变量 - 函数参数 - 中断/异常发生时的上下文保护(寄存器状态) --- ### **2. 顶(Stack Top)的定义** - **硬件关联**: Cortex-M内核使用**MSP(主指针)**或**PSP(进程指针)**寄存器指向当前顶。GD32默认使用MSP。 - **初始化位置**: 顶地址在启动文件(如`startup_gd32f5xx.s`)中通过链接脚本定义,通常位于RAM区域的**最高地址**(例如`0x2000C000`)。 - **代码体现**: 在启动代码中,顶通过符号(如`__initial_sp`)声明,并作为向量表的第一个条目: ```assembly g_pfnVectors: .word _estack ; 顶地址 .word Reset_Handler ; 复位向量 ... ``` --- ### **3. 底(Stack Base)的定义** - **计算方法**: 底 = 顶地址 - 空间大小。例如: 若顶为`0x2000C000`,大小为2KB(`0x800`),则底为`0x2000B800`。 - **链接脚本定义**: 在`.ld`文件中会指定大小(如`_STACK_SIZE = 0x800;`),并通过`_estack = .;`标记顶。 --- ### **4. 的增长方向** - **满递减(Full Descending)**: Cortex-M系列采用此模式,指针**先减后存**。 例如:执行`PUSH {R0}`时,MSP先减少4字节,再将R0存入新位置。 --- ### **5. 验证方法** - **查看启动文件**: 在`startup_gd32f5xx.s`中确认`_estack`的值,对应RAM结束地址。 - **调试观察**: 通过调试器读取`MSP`寄存器的值,即为当前顶地址。 --- ### **6. 注意事项** - **溢出风险**: 若超过预分配空间(如递归过深),会覆盖其他数据(如、全局变量),需合理设置大小。 - **多任务场景**: 若使用RTOS,每个任务需独立空间,此时PSP会被用于任务。 通过上述步骤,可清晰理解GD32F527中底的定义及实现方式。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值