堆和栈
首先堆和栈可以分为两种,一种是数据结构,另一种是和内存的分配有关,这两种虽然都有栈和堆,但是两者关系并不大,
4.1 数据结构中的堆栈以及队列
线性表是由n个相同类型的数据元素组成的有限序列,它是最基本的一种线性结构。顾名思义,线性表就像是一条线,不会分叉线性表有两种存储方式,分别是顺序存储和链式存储。==采用顺序存储的线性表称为顺序表,采用链式存储的线性表称为链表,链表在顺序表的基础上,增加了指向下一节点的地址,使得每个节点具有数据域和指针域==,链表又分为单链表、双向链表和循环链表。具体参考
4.1.1 堆和栈
堆和栈是一个东西,“堆栈” 其实说的就是栈而不是堆。 栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守:后进先出LIFO(Last In First Out)的原则(压子弹)。 压栈: 栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。 出栈: 栈的删除操作叫做出栈。出数据也在栈顶。
4.1.2 队列
和栈相反,队列是一种先进先出的线性表。它只允许在表的一端进行插入,而在另一端删除元素,允许插入的一端称为队尾,允许删除的一端则称为队头。
4.2 内存模型中的堆栈
内存中的堆和栈实际都是在SARM上,从储存器地址上看,是从0X20000000到0X3FFFFFFF的512MB大小空间(F103ZET6实际只有0X20000000到0X2000FFFF的64KB在使用,剩余的地址等待使用)
堆(Heap):人为申请手动释放,就是通过new、malloc、realloc分配的内存块,编译器不会负责它们的释放工作,需要用程序区释放。分配方式类似于数据结构中的链表。“内存泄漏”通常说的就是堆区。 栈(STACK): 存放函数内的局部变量,形参和函数返回值。栈区之中的数据的作用范围过了之后,系统就会回收自动管理栈区的内存(分配内存 , 回收内存),不需要开发人员来手动管理。栈区就像是一家客栈,里面有很多房间,客人来了之后自动分配房间,房间里的客人可以变动,是一种动态的数据变动。容易混淆堆栈?看完这篇,轻松区别堆与栈! - 知乎 (zhihu.com)
堆和栈都被包含在ZI-data数据段中
4.2.1 堆和栈的主要区别
1.内存地址增长的方向不同
堆是向着内存地址增加的方向增长的,从内存的低地址向高地址方向增长;
栈的增长方向与之相反,是向着内存地址减小的方向增长,由内存的高地址向低地址方向增长
假设一个程序的函数调用顺序为:主函数main调用函数func1,函数func1调用函数func2。当这个程序被操作系统调入内存运行时,其对应的进程在内存中的映射结果如下图所示,显然func1的地址高于func2,如下图所示(注意代码段在FLASH上)
堆的生长方向,都是向上的.在程序里面,所有的内存分为:堆+栈. 只是他们各自的起始地址和增长方向不同,他们没有一个固定的界限,所以一旦堆栈冲突,系统就到了崩溃的时候了.
2.申请的大小限制不同
栈是向低地址扩展的数据结构,是一块连续的内存区域,栈顶的地址和栈的最大容量是系统预先规定好的,能从栈获得的空间较小。
堆是向高地址扩展的数据结构,是不连续的内存区域,堆获得的空间比较灵活,也比较大。
但实际以上的说法是在win端上,其他环境也不一定,以STM32F103ZET6为例,在官方提供的启动文件中Stack_Size 为0x00000400,Heap_Size为0x00000200,也就是说栈1024字节,堆的大小为为512字节,栈的大小大于堆。
因此若工程中使用的局部变量较多,或嵌套关系复杂时,定义的数据长度较大时,若不调整栈的空间大小,则会导致程序出现栈溢出,程序运行结果与预期的不符或程序跑飞。这时我们就需要手动的调整栈的大小。
当工程中使用了malloc动态分配内存空间时,这时分配的空间就为堆的空间。所以若默认的堆空间大小不满足工程需求时,就需要手动调整堆空间的大小。
调整方法:
1 直接在启动文件中修改堆栈空间的大小
3.申请效率不同
栈由系统自动分配,速度快,但是程序员无法控制。
堆是有程序员自己分配,速度较慢,容易产生碎片,频繁执行malloc或free势必会造成内存空间的不连续,形成大量的碎片,使程序效率降低,不过用起来方便。
4.2.2 堆栈大小以及地址和堆栈指针地址的确定
用官方提供的启动文件时,打开map文件,到Local Symbols段的末尾,有如下信息,说明了msp指针指向地址0x20000788,堆HEAP区的大小为512字节,起始地址(堆向上增长,因此是最低地址)为0x20000188,栈STACK 区的大小为1024字节,起始地址(栈向下增长,因此是最高地址)为0x20000388
4.2.3 堆栈增长方向的确定程序
如果不确定增长方向,可以用以下程序测试
4.3大小端模式
大端模式:地位字节存在高地址上,高位字节存在低地址上。 小段模式:高位字节存在高地址上,低位字节存在低地址上。
stm32使用MDK编译时属于小端模式,简单的说,比如u32 temp=0x12345678; 假设temp地址在0x2000 0010。那么在内存里面,存放就变成了:
0x2000 0010
78 56 34 12
代码确认:
以__initial_sp的地址为例,0x20000798这个值是SP指针的值,应该写入0x08000000地址
使用调试器,在下方的地址查看栏输入0x08000000,发现下方的数据并不是0x20000798,而是反过来的,这是因为小端模式,高位字节存在高地址上,0x20000798的高位字节是20,而高地址是0x08000003,所以0x08000003地址上是20,以此类推。
(62条消息) STM32 堆栈大小详解 以及变量存储位置_allen6268198的博客-CSDN博客_stm32栈空间一般分配多大
Last updated