单片机的堆和栈(Heap & Stack)详解

摘要:
BSS是英语中BlockStartedbySymbol的缩写。简单地说,malloc在你使用它时会申请一些空间来使用,但不要忘记在使用后释放它。否则,堆溢出会经常发生,占用堆栈的空间并导致程序崩溃。

原文链接:https://blog.csdn.net/emoeror_zhang/article/details/94737249

单片机的堆和栈(Heap & Stack)详解
单片机的堆和栈(Heap & Stack)详解第1张
烛火飞蛾 2019-07-05 18:41:21 单片机的堆和栈(Heap & Stack)详解第2张 5844 单片机的堆和栈(Heap & Stack)详解第3张 收藏 40
分类专栏: 嵌入式C语言基础 文章标签: STM32 heap stack  
单片机的堆和栈(Heap & Stack)详解第4张
STM32CUBE+自平衡车从想法到产品
以实战为途径,使用STM32CUBE快速搭建自平衡小车项目。从想法到产品,介绍思路、设计、编程、、算法、调试、产品的每个过程详细思路,看完整个教程,每位朋友都能独立自主的设计属于自己的平衡车。
单片机的堆和栈(Heap & Stack)详解第5张烛火飞蛾
¥9.90
一、程序内存分配

由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
4、文字常量区—常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。

二、在单片机中内存如何储存

以STM32单片机为例:

单片机的堆和栈(Heap & Stack)详解第6张

                                                        STM32的地址空间映射图

我们可以看到代码存储区域在CODE区域;

STM32的堆栈是存放在片上静态SRAM中的,地址分配可以见Keil的编译map文件:

代码来源地址:https://download.csdn.net/download/emoeror_zhang/11286638

   HEAP                                     0x20000148   Section      512  startup_stm32f10x_hd.o(HEAP)
   STACK                                    0x20000348   Section     1024  startup_stm32f10x_hd.o(STACK)
    __initial_sp                             0x20000748   Data           0  startup_stm32f10x_hd.o(STACK)

上面节选中, HEAP是堆的基地址,__initial_sp  是栈指针 。示意图如下:
单片机的堆和栈(Heap & Stack)详解第7张   

 三、堆栈地址的设置

    在上述图和map中,我么可以看到堆的大小是0线0x200,也就是在0x20000148-0x20000348之间,而栈的地址大小是0x400,也就是在0x20000348-0x20000748。为什么他们的大小是这样的,是怎么由来的呢?

    打开汇编文件startup_stm32f10x_hd.s,我们可以找到相对应的设置堆栈大小的程序,如图:

单片机的堆和栈(Heap & Stack)详解第8张

    堆和栈,一般堆是由低地址往上增长,栈是由往下减少。都是连续的,C语言不提供内存保护机制类似的功能,如果堆一直增长,栈一直申请,然后就会导致栈溢出,从而导致程序崩溃。

四、变量储存位置分析

    同样的,我们还是以上述的map文件为例子进行分析。

   基础知识:   

  1.  
    .data: inited ,通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配
  2.  
     
  3.  
    .bss:zero,通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文BlockStarted by Symbol的简称。BSS段属于静态内存分配。
  4.  
     
  5.  
    cstack:函数局部变量用的区域,所有的功能函数使用的局部变量都是从这个堆栈申请使用的,用完了再还回去。子函数里面用到的局部变量都是在这里面取来用的

单片机的堆和栈(Heap & Stack)详解第9张

单片机的堆和栈(Heap & Stack)详解第10张

   如图所示,fac_ms和fac_us 在程序中是static变量类型,储存在0x20000000-0x20000148之间的位置,fac_ms在0x20000016,fac_us在0x20000014,那其他的地址处是什么数据呢?

   继续在map里面寻找,找到如下图所示:

单片机的堆和栈(Heap & Stack)详解第11张

单片机的堆和栈(Heap & Stack)详解第12张

     发现其余的地质处储存了全局变量数组,以及引用的库文件的全局变量。

     在map里面我们看到,全局变量和静态变量储存的位置,和堆栈无关,那么堆栈储存的内容是什么呢?

五、堆栈存放内容

1、栈区

      存放函数的参数值,局部变量的值等等临时变量,退出该作用域该临时变量就会自动释放。

2、堆区

     系统会给每个程序分配一部分栈空间让他们能够运行起来,问题就是栈空间必然存在不够用的问题,而堆不属于程序,堆是独立的,是公用的。只要你malloc(sizeof(SIZE_YOU_WANT)),就可以得到相应一部分的堆空间。

    简单的来说,就是当你使用的时候malloc申请一部分空间来使用,但是别忘记了使用完成之后free掉,不然往往会堆溢出,占用了栈的位置空间,导致程序奔溃。

总结:

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

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

免责声明:文章转载自《单片机的堆和栈(Heap & Stack)详解》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇opencv 彩色图像亮度、对比度调节 直方图均衡化在linux上oracle服务启动停止详细下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

(stm32f103学习总结)—stm32定时器中断

一、定时器介绍STM32F1的定时器非常多,由2个基本定时器(TIM6、TIM7)、4个通 用定时器(TIM2-TIM5)和2个高级定时器(TIM1、TIM8)组成。基本定 时器的功能最为简单,类似于51单片机内定时器。通用定时器是在基本 定时器的基础上扩展而来,增加了输入捕获与输出比较等功能。高级定 时器又是在通用定时器基础上扩展而来,增加了可编程死区互...

Golang---内存逃逸

  摘要:今天我们来了解一下 Golang 中的内存逃逸的概念。   引言:写过C/C++的同学都知道,调用著名的malloc和new函数可以在堆上分配一块内存,这块内存的使用和销毁的责任都在程序员。一不小心,就会发生内存泄露,搞得胆战心惊;切换到Golang后,基本不会担心内存泄露了。虽然也有new函数,但是使用new函数得到的内存不一定就在堆上。堆和栈...

【转】C++标准转换运算符static_cast

static_cast<new_type> (expression) 虽然const_cast是用来去除变量的const限定,但是static_cast却不是用来去除变量的static引用。其实这是很容易理解的,static决定的是一个变量的作用域和生命周期,比如:在一个文件中将变量定义为static,则说明这个变量只能在本Package中使用...

less和scss

一、less基础语法 1、声明变量:@变量名:变量值;      使用变量:@变量名; 2、混合(Mixins) 1)无参混合 声明: .class{} 调用:在选择器中,使用.class;直接调用 2)有参无默认值混合: 声明:.class(@param){...

JSP九大内置对象和四个作用域

JSP中九大内置对象为:request 请求对象  类型 javax.servlet.ServletRequest 作用域 Requestresponse 响应对象 类型 javax.servlet.SrvletResponse...

_cdecl与_stdcall的区别

1. _cdecl  (1). 是C Declaration的缩写,表示C语言默认的函数调用方法,实际上也是C++的默认的函数调用方法。 (2). 所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈。 具体所示:调用方的函数调用->被调用函数的执行->被调用函数的结果返回->调用方清除调整堆栈。 (3). 被调用函数无需要求调...