OpenEdv-开源电子网

 找回密码
 立即注册

扫一扫,访问微社区

正点原子全套STM32开发资料,上千讲STM32视频教程,RT1052教程免费下载啦...

查看: 43531|回复: 193

STM32 大小端模式 与 堆栈及其增长方向分析

  [复制链接]

  离线 

500

主题

9万

帖子

31

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
129741
金钱
129741
注册时间
2010-12-1
在线时间
1169 小时
发表于 2013-11-16 14:17:02 | 显示全部楼层 |阅读模式
正点原子公众号

栈增长和大端/小端问题是和CPU相关的两个问题.

1,首先来看:栈(STACK)的问题.

函数的局部变量,都是存放在"栈"里面,栈的英文是:STACK.STACK的大小,我们可以在stm32的启动文件里面设置,以战舰stm32开发板为例,在startup_stm32f10x_hd.s里面,开头就有:

Stack_Size      EQU     0x00000800

表示栈大小是0X800,也就是2048字节.这样,CPU处理任务的时候,函数局部变量做多可占用的大小就是:2048字节,注意:是所有在处理的函数,包括函数嵌套,递归,等等,都是从这个"栈"里面,来分配的.
所以,如果一个函数的局部变量过多,比如在函数里面定义一个u8 buf[512],这一下就占了1/4的栈大小了,再在其他函数里面来搞两下,程序崩溃是很容易的事情,这时候,一般你会进入到hardfault....
这是初学者非常容易犯的一个错误.切记不要在函数里面放N多局部变量,尤其有大数组的时候!

对于栈区,一般栈顶,也就是MSP,在程序刚运行的时候,指向程序所占用内存的最高地址.比如附件里面的这个程序序,内存占用如下图:


图中,我们可以看到,程序总共占用内存:20+2348字节=2368=0X940
那么程序刚开始运行的时候:MSP=0X2000 0000+0X940=0X2000 0940.
事实上,也是如此,如图:


图中,MSP就是:0X2000 0940.
程序运行后,MSP就是从这个地址开始,往下给函数的局部变量分配地址.

再说说栈的增长方向,我们可以用如下代码测试:

//保存栈增长方向
//0,向下增长;1,向上增长.
static u8 stack_dir;

//查找栈增长方向,结果保存在stack_dir里面.
void find_stack_direction(void)
{
    static u8 *addr=NULL; //用于存放第一个dummy的地址。
    u8 dummy;               //用于获取栈地址
    if(addr==NULL)    //第一次进入
    {                         
        addr=&dummy;     //保存dummy的地址
        find_stack_direction ();  //递归
    }else                //第二次进入
 {  
        if(&dummy>addr)stack_dir=1; //第二次dummy的地址大于第一次dummy,那么说明栈增长方向是向上的.
        else stack_dir=0;           //第二次dummy的地址小于第一次dummy,那么说明栈增长方向是向下的. 
 }
}

这个代码不是我写的,网上抄来的,思路很巧妙,利用递归,判断两次分配给dummy的地址,来比较栈是向下生长,还是向上生长.
如果你在STM32测试这个函数,你会发现,STM32的栈,是向下生长的.事实上,一般CPU的栈增长方向,都是向下的.

2,再来说说,堆(HEAP)的问题.

全局变量,静态变量,以及内存管理所用的内存,都是属于"堆"区,英文名:"HEAP"
与栈区不同,堆区,则从内存区域的起始地址,开始分配给各个全局变量和静态变量.
堆的生长方向,都是向上的.在程序里面,所有的内存分为:堆+栈. 只是他们各自的起始地址和增长方向不同,他们没有一个固定的界限,所以一旦堆栈冲突,系统就到了崩溃的时候了.
同样,我们用附件里面的例程测试:



stack_dir的地址是0X20000004,也就是STM32的内存起始端的地址.
这里本来应该是从0X2000 0000开始分配的,但是,我仿真发现0X2000 0000总是存放:0X2000 0398,这个值,貌似是MSP,但是又不变化,还请高手帮忙解释下.
其他的,全局变量,则依次递增,地址肯定大于0X20000004,比如cpu_endian的地址就是0X20000005.
这就是STM32内部堆的分配规则.

3,再说说,大小端的问题.
大端模式:低位字节存在高地址上,高位字节存在低地址上
小端模式:高位字节存在高地址上,低位字节存在低地址上

STM32属于小端模式,简单的说,比如u32 temp=0X12345678;
假设temp地址在0X2000 0010.
那么在内存里面,存放就变成了:
地址              |            HEX         |
0X2000 0010  |  78   56   43  12  |

CPU到底是大端还是小端,可以通过如下代码测试:
//CPU大小端
//0,小端模式;1,大端模式.
static u8 cpu_endian;

//获取CPU大小端模式,结果保存在cpu_endian里面
void find_cpu_endian(void)
{
 int x=1;
 if(*(char*)&x==1)cpu_endian=0; //小端模式
 else cpu_endian=1;    //大端模式 
}
以上测试,在STM32上,你会得到cpu_endian=0,也就是小端模式.


3,最后说说,STM32内存的问题.
    还是以附件工程为例,在前面第一个图,程序总共占用内存:20+2348字节,这么多内存,到底是怎么得来的呢?
我们可以双击Project侧边栏的:Targt1,会弹出test.map,在这个里面,我们就可以清楚的知道这些内存到底是怎么来的了.在这个test.map最后,Image 部分有:
==============================================================================
Image component sizes

      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name
       172         10          0          4          0        995   delay.o//delay.c里面,fac_us和fac_ms,共占用4字节
       112         12          0          0          0        427   led.o
        72         26        304          0       2048        828   startup_stm32f10x_hd.o  //启动文件,里面定义了Stack_Size为0X800,所以这里是2048.
       712         52          0          0          0       2715   sys.o
       348        154          0          6          0     208720   test.o//test.c里面,stack_dir和cpu_endian 以及*addr  ,占用6字节.
       384         24          0          8        200       3050   usart.o//usart.c定义了一个串口接收数组buffer,占用200字节.
    ----------------------------------------------------------------------
      1800        278        336         20       2248     216735   Object Totals //总共2248+20字节
         0          0         32          0          0          0   (incl. Generated)
         0          0          0          2          0          0   (incl. Padding)//2字节用于对其
    ----------------------------------------------------------------------
      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Library Member Name
         8          0          0          0          0         68   __main.o
       104          0          0          0          0         84   __printf.o
        52          8          0          0          0          0   __scatter.o
        26          0          0          0          0          0   __scatter_copy.o
        28          0          0          0          0          0   __scatter_zi.o
        48          6          0          0          0         96   _printf_char_common.o
        36          4          0          0          0         80   _printf_char_file.o
        92          4         40          0          0         88   _printf_hex_int.o
       184          0          0          0          0         88   _printf_intcommon.o
         0          0          0          0          0          0   _printf_percent.o
         4          0          0          0          0          0   _printf_percent_end.o
         6          0          0          0          0          0   _printf_x.o
        12          0          0          0          0         72   exit.o
         8          0          0          0          0         68   ferror.o
         6          0          0          0          0        152   heapauxi.o
         2          0          0          0          0          0   libinit.o
         2          0          0          0          0          0   libinit2.o
         2          0          0          0          0          0   libshutdown.o
         2          0          0          0          0          0   libshutdown2.o
         8          4          0          0         96         68   libspace.o          //库文件(printf使用),占用了96字节
        24          4          0          0          0         84   noretval__2printf.o
         0          0          0          0          0          0   rtentry.o
        12          0          0          0          0          0   rtentry2.o
         6          0          0          0          0          0   rtentry4.o
         2          0          0          0          0          0   rtexit.o
        10          0          0          0          0          0   rtexit2.o
        74          0          0          0          0         80   sys_stackheap_outer.o
         2          0          0          0          0         68   use_no_semi.o
         2          0          0          0          0         68   use_no_semi_2.o
       450          8          0          0          0        236   faddsub_clz.o
       388         76          0          0          0         96   fdiv.o
        62          4          0          0          0         84   ffixu.o
        38          0          0          0          0         68   fflt_clz.o
       258          4          0          0          0         84   fmul.o
       140          4          0          0          0         84   fnaninf.o
        10          0          0          0          0         68   fretinf.o
         0          0          0          0          0          0   usenofp.o
    ----------------------------------------------------------------------
      2118        126         42          0        100       1884   Library Totals  //调用的库用了100字节.
        10          0          2          0          4          0   (incl. Padding)   //用于对其多占用了4个字节
    ----------------------------------------------------------------------
      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Library Name
       762         30         40          0         96       1164   c_w.l
      1346         96          0          0          0        720   fz_ws.l
    ----------------------------------------------------------------------
      2118        126         42          0        100       1884   Library Totals
    ----------------------------------------------------------------------
==============================================================================

      Code (inc. data)   RO Data    RW Data    ZI Data      Debug  
      3918        404        378         20       2348     217111   Grand Totals
      3918        404        378         20       2348     217111   ELF Image Totals
      3918        404        378         20          0          0   ROM Totals
==============================================================================
    Total RO  Size (Code + RO Data)                 4296 (   4.20kB)
    Total RW  Size (RW Data + ZI Data)              2368 (   2.31kB)   //总共占用:2248+20+100=2368.
    Total ROM Size (Code + RO Data + RW Data)       4316 (   4.21kB)
==============================================================================

通过这个文件,我们就可以分析整个内存,是怎么被占用的,具体到每个文件,占用多少.一目了然了.

4,最后,看看整个测试代码:
main.c代码如下,工程见附件.
#include "sys.h"
#include "usart.h"  
#include "delay.h" 
#include "led.h"
#include "beep.h"   
#include "key.h"   
//ALIENTEK战舰STM32开发板堆栈增长方向以及CPU大小端测试
//保存栈增长方向
//0,向下增长;1,向上增长.
static u8 stack_dir;
//CPU大小端
//0,小端模式;1,大端模式.
static u8 cpu_endian;
 

//查找栈增长方向,结果保存在stack_dir里面.
void find_stack_direction(void)
{
    static u8 *addr=NULL; //用于存放第一个dummy的地址。
    u8 dummy;               //用于获取栈地址
    if(addr==NULL)    //第一次进入
    {                         
        addr=&dummy;     //保存dummy的地址
        find_stack_direction ();  //递归
    }else                //第二次进入
 {  
        if(&dummy>addr)stack_dir=1; //第二次dummy的地址大于第一次dummy,那么说明栈增长方向是向上的.
        else stack_dir=0;           //第二次dummy的地址小于第一次dummy,那么说明栈增长方向是向下的. 
 }
}
//获取CPU大小端模式,结果保存在cpu_endian里面
void find_cpu_endian(void)
{
 int x=1;
 if(*(char*)&x==1)cpu_endian=0; //小端模式
 else cpu_endian=1;    //大端模式 
}
int main(void)
{   
 Stm32_Clock_Init(9); //系统时钟设置
 uart_init(72,9600);   //串口初始化为9600
 delay_init(72);       //延时初始化
 LED_Init();      //初始化与LED连接的硬件接口 
    printf("stack_dir:%x\r\n",&stack_dir);
    printf("cpu_endian:%x\r\n",&cpu_endian);
 
 find_stack_direction(); //获取栈增长方式
 find_cpu_endian();  //获取CPU大小端模式
  while(1)
 {
  if(stack_dir)printf("STACK DIRCTION:向上生长\r\n\r\n");
  else printf("STACK DIRCTION:向下生长\r\n\r\n");
  if(cpu_endian)printf("CPU ENDIAN:大端模式\r\n\r\n");
  else printf("CPU ENDIAN:小端模式\r\n\r\n");
  delay_ms(500);
  LED0=!LED0; 
 } 
}
测试结果如图:






 

ST32堆栈增长方向及大小端测试代码.rar

42.52 KB, 下载次数: 3756

我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复

使用道具 举报

  离线 

28

主题

1499

帖子

0

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
1652
金钱
1652
注册时间
2013-7-24
在线时间
0 小时
发表于 2013-11-17 21:57:57 | 显示全部楼层
Heap, 原子把全局变量等划入,我个人理解是静态分配的内存都算不得堆里。
于20150522停用该账号:http://www.microstar.club
回复 支持 1 反对 0

使用道具 举报

  离线 

2

主题

1405

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1953
金钱
1953
注册时间
2010-12-16
在线时间
124 小时
发表于 2013-11-16 15:59:21 | 显示全部楼层
对于单片机这种封闭代码的运行平台,内存分配有2个大方向,一个是静态变量,一个是动态变量,具体到作用域,又分为局部变量和全局变量.

全局静态变量:不管是否调用,它都在那里,比如LZ示例<test.c>的 line:11 和 line:15,注意这里加了<static>关键字,指明这个变量是并不是真正意义的全局变量,只是在这个文件的所有位置<声明位置以后的所有位置>可用.

局部静态变量:和全局静态变量类似,也是不管拉不拉屎先占坑的货,比如LZ示例<test.c>的 line:23 .特点是加了关键字<static>,意思是在这个位置,它是唯一的.在<find_stack_direction>函数里使用了递归,但局部静态变量是不在递归里重新分配空间的,原子也是通过这个方式来判断两次进入之间的地址关系.

局部动态变量:这个是最常见的,比如LZ示例<test.c>的 line:24,在这个示例里,每次声明<神灯啊神灯>,结果出来的都是新的神灯,许了愿就溜掉,是这种变量的特点.它不会记得它曾经是什么.注意,由于每次都喝了孟婆汤,有经验的码农会在召唤时默认赋一个初值,避免出现不可预料的使用.

全局动态变量:存在吗?全局可见但又可以踢掉的奇葩吗?抱歉,这句话对<全局>是个误解.<全局>的意思是变量本身没有编译器指定的生命周期,也就是<作用域>,但还有代码指定的生命周期.在LZ的示例里,<堆>就是这么一个东西,代码说<你在>就在,<你不在>就不在.申请了堆后,只要谁(任何位置的代码)知道这个位置是可以用的,谁都可以用(**具有进程内存保护的平台除外**),即使申请空间的变量<挂了>,这个空间也一直存在,直到有代码把它<销毁>掉.

顺便推销老帖http://www.openedv.com/posts/list/19693.htm

修改+注释.
**新的linux把uclinux统一了,不知道是否在单片机实现进程内存保护,同求证.不过这也不在<封闭代码平台>这个前提下了.



技术讨论请发帖 , 需要我回复请点左下的 < 回复 > 让系统通知我 . 本人不通过其他方式返回任何参数.
回复 支持 1 反对 0

使用道具 举报

  离线 

9

主题

156

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
212
金钱
212
注册时间
2013-9-24
在线时间
0 小时
发表于 2013-11-16 14:31:02 | 显示全部楼层
这么给力的东西。。。怎么能不顶呢。。一目了然啊。。。。胜读十年书啊。。赞一个
人需要沐浴阳光,也必将经历风雨
回复 支持 反对

使用道具 举报

  离线 

15

主题

182

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
337
金钱
337
注册时间
2013-4-23
在线时间
22 小时
发表于 2013-11-16 14:48:12 | 显示全部楼层
正点原子公众号
酷,学习了
回复 支持 反对

使用道具 举报

  离线 

500

主题

9万

帖子

31

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
129741
金钱
129741
注册时间
2010-12-1
在线时间
1169 小时
 楼主| 发表于 2013-11-16 14:54:20 | 显示全部楼层
目前还有个问题没解决,就是我提到的,0X2000 0000最开始的4个字节,存储的0X2000 0938,到底是怎么回事?
请高手指教.
共同学习.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

  离线 

83

主题

349

帖子

1

精华

高级会员

Rank: 4

积分
907
金钱
907
注册时间
2012-8-10
在线时间
13 小时
发表于 2013-11-16 15:18:39 | 显示全部楼层
回复【4楼】正点原子:
---------------------------------
我靠,分析的如此透彻,我都不敢看了,佩服
回复 支持 反对

使用道具 举报

  离线 

46

主题

160

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
398
金钱
398
注册时间
2011-4-11
在线时间
7 小时
发表于 2013-11-16 15:24:02 | 显示全部楼层
非常棒的理解方式。
回复 支持 反对

使用道具 举报

  离线 

2

主题

1405

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1953
金钱
1953
注册时间
2010-12-16
在线时间
124 小时
发表于 2013-11-16 15:31:48 | 显示全部楼层
回复【4楼】正点原子:
---------------------------------
见test.map ->line:421和test.c -> line:23
技术讨论请发帖 , 需要我回复请点左下的 < 回复 > 让系统通知我 . 本人不通过其他方式返回任何参数.
回复 支持 反对

使用道具 举报

  离线 

1

主题

7

帖子

0

精华

新手入门

积分
38
金钱
38
注册时间
2012-10-11
在线时间
3 小时
发表于 2013-11-16 16:37:45 | 显示全部楼层
大端模式:高位字节存在高地址上,低位字节存在低地址上
小端模式:低位字节存在高地址上,高位字节存在低地址上 

这个定义是错误的写反了

http://baike.baidu.com/link?url=NvqoEub077HLSI62HipOCDo1jOyZRHIFi8S2L2AVLOzbeaXyRpSgDu_DCPJvdLMFHkiv2y-qqZ8vw-lT3J5zNa

回复 支持 反对

使用道具 举报

头像被屏蔽

  离线 

6168

主题

7041

帖子

1

精华

论坛元老

Rank: 8Rank: 8

积分
19700
金钱
19700
注册时间
2012-12-27
在线时间
23 小时
发表于 2013-11-16 16:42:00 | 显示全部楼层
感谢分享,版区有你更精彩!
回复 支持 反对

使用道具 举报

  离线 

2

主题

1405

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1953
金钱
1953
注册时间
2010-12-16
在线时间
124 小时
发表于 2013-11-16 17:03:57 | 显示全部楼层
回复【楼主位】正点原子:
---------------------------------
全局变量,静态变量,以及内存管理所用的内存,都是属于"堆"区,英文名:"HEAP"
与栈区不同,堆区,则从内存区域的起始地址,开始分配给各个全局变量和静态变量.
堆的生长方向,都是向上的.在程序里面,所有的内存分为:堆+栈. 只是他们各自的起始地址和增长方向不同,他们没有一个固定的界限,所以一旦堆栈冲突,系统就到了崩溃的时候了.
同样,我们用附件里面的例程测试:

stack_dir的地址是0X20000004,也就是STM32的内存起始端的地址.
这里本来应该是从0X2000 0000开始分配的,但是,我仿真发现0X2000 0000总是存放:0X2000 0398,这个值,貌似是MSP,但是又不变化,还请高手帮忙解释下.
其他的,全局变量,则依次递增,地址肯定大于0X20000004,比如cpu_endian的地址就是0X20000005.
这就是STM32内部堆的分配规则.
-----------------------------------------------------------------------
这里可能被测试代码误导了,见6L的说明.

在<这个链接器>里,堆空间并不是在内存起始空间开始的.7L的介绍也不完整.

见例子里的<test.map>line:430...434,堆和栈的<标号>地址是同一个位置,但实际上是编译器把堆和栈放在一起.由于7L链接讨论的原因,默认没有任何机制可以检查栈是否溢出.如果栈溢出,首先会破坏堆,然后再破坏静态变量,最后突破静态变量就会进入非内存区,此时将触发数据访问错误.

但是,单单有上面的错误是不行的,比如很多例子,只要稍微减少栈的用量就<好像>不会进hardfault,但实际上静态变量还是可能出现局部破坏,这种错误很隐蔽,即使调试也只是发现变量无端被修改,但找不到被<哪段代码>修改了,即使使用<内存访问断点>,也只是查到进入某个函数的<汇编代码过程中>出现修改,这即使对有一定经验的人员来看也是很郁闷的.

当然,如果使用了堆空间,那问题会比较快发现,但也是<发现>而已.

广告:7L的链接是发现并可靠确认栈溢出的方法之一.

堆没有增长方向一说,它是由程序动态分配的,比如LZ的<内存管理示例>,就是自己创建一个自己管理的堆,或者是编译器自己不开源的堆管理器.

堆和栈都是用来放置<动态变量>的内存区域,它们都是先占一些内存,然后谁用就给谁,用完了还回来的机制.如7L所述,堆用于管理<全局动态变量>,由具体的代码管理,栈用于管理<局部动态变量>,有具体的汇编指令通过硬件分配.

静态变量和局部动态变量的分配顺序,本身并没有规范,只是链接器一般设计是顺序分配,用户也可以通过预编译字和分散加载文件调整分配顺序.堆的分配,依据堆管理算法而定,由于有碎片抑制等算法,可以认为是乱序分配.如pony279的例子,还可以在分配后动态调整.
技术讨论请发帖 , 需要我回复请点左下的 < 回复 > 让系统通知我 . 本人不通过其他方式返回任何参数.
回复 支持 反对

使用道具 举报

  离线 

500

主题

9万

帖子

31

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
129741
金钱
129741
注册时间
2010-12-1
在线时间
1169 小时
 楼主| 发表于 2013-11-16 17:10:36 | 显示全部楼层
回复【9楼】barryzxy:
---------------------------------
谢谢指出,已经改过。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

  离线 

500

主题

9万

帖子

31

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
129741
金钱
129741
注册时间
2010-12-1
在线时间
1169 小时
 楼主| 发表于 2013-11-16 17:26:22 | 显示全部楼层
回复【7楼】shihantu:
---------------------------------
确实,原来是从0X2000 0000开始分配,只是0X2000 0000分配给addr了...
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

  离线 

20

主题

122

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
222
金钱
222
注册时间
2013-9-21
在线时间
0 小时
发表于 2013-11-16 18:02:55 | 显示全部楼层
顶,总结的太好了。
回复 支持 反对

使用道具 举报

  离线 

0

主题

8

帖子

0

精华

新手入门

积分
28
金钱
28
注册时间
2013-3-20
在线时间
0 小时
发表于 2013-11-16 19:27:52 | 显示全部楼层
 mark
回复 支持 反对

使用道具 举报

  离线 

6

主题

153

帖子

0

精华

初级会员

Rank: 2

积分
197
金钱
197
注册时间
2013-4-4
在线时间
0 小时
发表于 2013-11-16 21:15:42 | 显示全部楼层
写的真好!不过原子可以考虑把字改大一号,论坛默认的字号看起来略吃力,虽然可以CTRL+滚轮调整,但是还是很不方便。
~~~~ 欢迎讨论,拒绝谩骂 ~~~~
回复 支持 反对

使用道具 举报

  离线 

500

主题

9万

帖子

31

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
129741
金钱
129741
注册时间
2010-12-1
在线时间
1169 小时
 楼主| 发表于 2013-11-16 21:24:07 | 显示全部楼层
回复【16楼】兰斯洛:
---------------------------
好。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

  离线 

0

主题

91

帖子

0

精华

初级会员

Rank: 2

积分
134
金钱
134
注册时间
2013-8-25
在线时间
11 小时
发表于 2013-11-16 22:11:42 | 显示全部楼层
我顶下
回复 支持 反对

使用道具 举报

  离线 

38

主题

2067

帖子

6

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3263
金钱
3263
注册时间
2012-1-16
在线时间
36 小时
发表于 2013-11-16 22:24:55 | 显示全部楼层

受shihantu启发,在原子哥的基础上加入栈容量检测,欢迎大家指正

#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h" 
#include "beep.h"  
#include "key.h"  
//ALIENTEK战舰STM32开发板堆栈增长方向以及CPU大小端测试

//保存栈增长方向
//0,向下增长;1,向上增长.
static u8 stack_dir;

//CPU大小端
//0,小端模式;1,大端模式.
static u8 cpu_endian;

static u32 STACK_UP_VAL=0; //栈顶指针

#define STACK_SIZE 0x00000400 //栈大小与启动文件分配大小的一致
#define get_current_stack_size() (STACK_SIZE-STACK_UP_VAL+GET_MSP()) //得到当前栈的剩余容量
#define init_stack_up_pointer() (STACK_UP_VAL = GET_MSP())//初始化栈顶指针



//查找栈增长方向,结果保存在stack_dir里面.
void find_stack_direction(void)
{
    static u8 *addr=NULL; //用于存放第一个dummy的地址。
    u8 dummy;               //用于获取栈地址 
    if(addr==NULL)   //第一次进入
    {                          
        addr=&dummy;   //保存dummy的地址
        find_stack_direction ();  //递归 
    }else               //第二次进入 
{
        if(&dummy>addr)stack_dir=1; //第二次dummy的地址大于第一次dummy,那么说明栈增长方向是向上的. 
        else stack_dir=0;           //第二次dummy的地址小于第一次dummy,那么说明栈增长方向是向下的.  
}


//获取CPU大小端模式,结果保存在cpu_endian里面
void find_cpu_endian(void)
int x=1;
if(*(char*)&x==1)cpu_endian=0; //小端模式 
else cpu_endian=1; //大端模式  





//栈溢出测试
void stack_overflow_test(void){
int i=0;
u8 a[300];
printf("当前栈的剩余容量--------------current_stack_size:%d\r\n",get_current_stack_size());
for(i=0; i<300; i++){
a = 1;
}
}

int main(void)
{  
Stm32_Clock_Init(9); //系统时钟设置
uart_init(72,9600); //串口初始化为9600
delay_init(72);   //延时初始化 
init_stack_up_pointer(); //初始化栈顶指针
printf("当前栈顶指针------------current_msp:%x\r\n",STACK_UP_VAL);
stack_overflow_test(); //栈溢出测试
LED_Init();   //初始化与LED连接的硬件接口  
    printf("stack_dir:%x\r\n",&stack_dir);
    printf("cpu_endian:%x\r\n",&cpu_endian);
find_stack_direction(); //获取栈增长方式
find_cpu_endian(); //获取CPU大小端模式

  while(1)
{
if(stack_dir)printf("STACK DIRCTION:向上生长\r\n\r\n");
else printf("STACK DIRCTION:向下生长\r\n\r\n");
if(cpu_endian)printf("CPU ENDIAN:大端模式\r\n\r\n");
else printf("CPU ENDIAN:小端模式\r\n\r\n"); 
delay_ms(500);
LED0=!LED0;  
}  
}

//硬件错误处理
void HardFault_Handler(void)
{
u32 i;
u8 t=0;
u32 temp;
temp=SCB->CFSR; //fault状态寄存器(@0XE000ED28)包括:MMSR,BFSR,UFSR
  printf("CFSR:%8X\r\n",temp); //显示错误值
temp=SCB->HFSR; //硬件fault状态寄存器
  printf("HFSR:%8X\r\n",temp); //显示错误值
  temp=SCB->DFSR; //调试fault状态寄存器
  printf("DFSR:%8X\r\n",temp); //显示错误值
    temp=SCB->AFSR; //辅助fault状态寄存器
  printf("AFSR:%8X\r\n",temp); //显示错误值
  LED1=!LED1;
  while(t<5)
{
t++;
LED0=!LED0;
//BEEP=!BEEP;
for(i=0;i<0X1FFFFF;i++);
  }

}

ST32堆栈增长方向及大小端测试代码+栈容量检测.rar

41.02 KB, 下载次数: 2718

站在巨人的肩膀上不断的前进。。。
回复 支持 反对

使用道具 举报

  离线 

14

主题

173

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
255
金钱
255
注册时间
2011-10-14
在线时间
1 小时
发表于 2013-11-16 22:55:46 | 显示全部楼层
原子哥,多谢分享,真可以以“导师”称呼你!
回复 支持 反对

使用道具 举报

  离线 

14

主题

173

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
255
金钱
255
注册时间
2011-10-14
在线时间
1 小时
发表于 2013-11-16 23:24:34 | 显示全部楼层
回复【19楼】xiaoyan:
---------------------------------
//栈溢出测试
void stack_overflow_test(void){
int i=0;
u8 a[300];
printf("当前栈的剩余容量--------------current_stack_size:%d\r\n",get_current_stack_size());
for(i=0; i<300; i++){
a = 1;
}

}

学习你的例程,发现在函数调用里面,额外的消耗了8个bytes的Stack,应该是函数调用的消耗,但不知道具体是如何消耗的?
回复 支持 反对

使用道具 举报

  离线 

38

主题

2067

帖子

6

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3263
金钱
3263
注册时间
2012-1-16
在线时间
36 小时
发表于 2013-11-17 00:03:26 | 显示全部楼层
回复【21楼】smithlin:
---------------------------------
那个 int i=0;好像并没有分配空间,这个函数消耗了312个字节,那12个字节我也不知道哪去了,貌似和printf有关,因为我不用“i”这个变量的时候MSP的值一样,有点搞不清了,坐等大神
站在巨人的肩膀上不断的前进。。。
回复 支持 反对

使用道具 举报

  离线 

14

主题

173

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
255
金钱
255
注册时间
2011-10-14
在线时间
1 小时
发表于 2013-11-17 09:10:54 | 显示全部楼层
回复【22楼】xiaoyan:
---------------------------------
确实:
1. int i =0不知道跑到哪里去了
2. 把printf去掉就只多8bytes了
回复 支持 反对

使用道具 举报

  离线 

14

主题

173

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
255
金钱
255
注册时间
2011-10-14
在线时间
1 小时
发表于 2013-11-17 09:23:01 | 显示全部楼层
我感觉这8个字节与函数stack_overflow_test地址相关,实际跟踪下来内容与stack_overflow_test地址比较相近,但不是完全一样。
回复 支持 反对

使用道具 举报

  离线 

20

主题

122

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
222
金钱
222
注册时间
2013-9-21
在线时间
0 小时
发表于 2013-11-17 09:59:56 | 显示全部楼层
回复【22楼】xiaoyan:
---------------------------------
http://blog.csdn.net/zhoucoolqi/article/details/7540612
回复 支持 反对

使用道具 举报

  离线 

5

主题

154

帖子

0

精华

初级会员

Rank: 2

积分
198
金钱
198
注册时间
2013-6-8
在线时间
1 小时
发表于 2013-11-17 12:23:13 | 显示全部楼层
回复【21楼】smithlin:
---------------------------------
8byte 的消耗来源于 push  {Rn,PC} , 入栈两个32位的,当然会有8byte的消耗啦
即使爬到最高的山上,一次也只能脚踏实地地迈一步。
回复 支持 反对

使用道具 举报

  离线 

38

主题

2067

帖子

6

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3263
金钱
3263
注册时间
2012-1-16
在线时间
36 小时
发表于 2013-11-17 13:53:47 | 显示全部楼层
312个字节哪去了,我的分析。。。,欢迎大家指证^_^




站在巨人的肩膀上不断的前进。。。
回复 支持 反对

使用道具 举报

  离线 

38

主题

2067

帖子

6

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3263
金钱
3263
注册时间
2012-1-16
在线时间
36 小时
发表于 2013-11-17 13:56:14 | 显示全部楼层
回复【24楼】smithlin:
---------------------------------
看我的分析
站在巨人的肩膀上不断的前进。。。
回复 支持 反对

使用道具 举报

  离线 

5

主题

154

帖子

0

精华

初级会员

Rank: 2

积分
198
金钱
198
注册时间
2013-6-8
在线时间
1 小时
发表于 2013-11-17 14:59:11 | 显示全部楼层

你这只是单层调用,情况很简单,你这个例子不是特别好

根据ATPCS的规定,可以推测应该是下面这样的。

不看那个printf, 调用stack_overflow_test()的过程是这样的
=================================================================

     bl stack_overflow_test  ;lr保存断点地址, pc装入stack_overflow_test地址,并跳转过去


stack_overflow_test:
     push {rn...,lr}   ;lr是必须压栈的,至于会压几个rn寄存器,得看编译器心情,多消耗了12字节的话,应该只压了

                       ;两个rn寄存器,再加上lr嘛,当然是12字节啦
     sub  sp,sp,300    ;sp会减300字节,a[]是向上增长的,也就是a[0]是在低地址,a[299]在高地址啦
    
     循环给a[]赋值     ;i变量绝对是存在r4~r11的某一个,具体哪个就不知道了,极简单的循环赋值

     add sp,sp,300
     pop  {rn...,pc}    ;跟上面是对称的,pop可是pc了哦

==================================================================


局部变量压栈也得看寄存器使用情况的和函数内部语句的复杂程度,如下,
int func(int a)
{
    int c=6, b=8;
    return a+b+c;
}

int main(void)
{
   int a=0, b=1, c=2, d=8, e=9;
   a = b;
   b = c;
   c = a;
   e = a*b;
   d = c*b;
 
 for(;;)
 {
  a = a*b;
  b = b*c;
  c = b*a;
  c = func(e);/*进入func()后,main的a,b,c,d,e会压栈吗?答:不会*/
  d = a*c;
  e = d*c;
 }
}

/*再比如,这里只有recursive的a和recursive函数的地址会压栈4次,注意只有4次,
main中的a,b,c,d,e还是没有压栈,一直保留在寄存器中,仿真即可看出*/
void recursive(void)
{
    static int cnt=5;
    int a=0;//如果这里增加到int a=0, b=1, c=2, d=3, e=4;那main函数的变量就会压栈了
 
   if (cnt>0)
   {
        a = --cnt;
        recursive();
    }
}
int main(void)
{
 int a=0, b=1, c=2, d=8, e=9;
 a = b;
 b = c;
 c = a;
 e = a*b;
 d = c*b;
 
 for(;;)
 {
  a = a*b;
  b = b*c;
  c = b*a;
  recursive();/*调用进去后,主函数的a,b,c,d,e是否会压栈? 答:还是不会,因为寄存器足够用,没必要压栈,就算你递归50次也不会*/
  d = a*c;
  e = d*c;
 }
}

 


 

ATPCS介绍与使用.pdf

455.21 KB, 下载次数: 2630

即使爬到最高的山上,一次也只能脚踏实地地迈一步。
回复 支持 反对

使用道具 举报

  离线 

38

主题

2067

帖子

6

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3263
金钱
3263
注册时间
2012-1-16
在线时间
36 小时
发表于 2013-11-17 15:33:53 | 显示全部楼层

应该是我弄错了,可以看到下面,R4:0x0000000 R5:0x200000E0 LR:0x080002E7





站在巨人的肩膀上不断的前进。。。
回复 支持 反对

使用道具 举报

  离线 

2

主题

1405

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1953
金钱
1953
注册时间
2010-12-16
在线时间
124 小时
发表于 2013-11-17 15:36:33 | 显示全部楼层
回复【29楼】倒拔萝卜:
---------------------------------
对于初学者来说,LZ的例子是比较普遍的情况了.你的反而是高级技巧啦..

既然26L如此深入敌阵,拉出来讨论也是正常的,这样才全面.
技术讨论请发帖 , 需要我回复请点左下的 < 回复 > 让系统通知我 . 本人不通过其他方式返回任何参数.
回复 支持 反对

使用道具 举报

  离线 

38

主题

2067

帖子

6

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3263
金钱
3263
注册时间
2012-1-16
在线时间
36 小时
发表于 2013-11-17 15:45:02 | 显示全部楼层
回复【31楼】shihantu:
---------------------------------
嗯,主要是对汇编不怎么熟悉,所以有时候只能瞎猜,哈哈
站在巨人的肩膀上不断的前进。。。
回复 支持 反对

使用道具 举报

  离线 

2

主题

1405

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1953
金钱
1953
注册时间
2010-12-16
在线时间
124 小时
发表于 2013-11-17 15:50:56 | 显示全部楼层
回复【30楼】xiaoyan:
---------------------------------
这个话题比较麻烦.

如28L所述,获取容量的操作尽量不要触发数据入栈,才能<准确地获得调用者>的栈指针,或者是另一个概念,把操作本身所需要的栈也算进去,因为的确<此时此刻>用了这么多的栈.

但,你的例子没有考虑printf的内的栈操作,也处理不了这个栈操作,也就是说,可能会在printf内触发hardfault,但代码捕捉不到,因为不能在里面插入代码.在fputc插入代码是可以,如前所述,不知道printf的结构,也不能确定fputc是入栈最深处.

对于我个人来说,更倾向于如何实用化,在应用代码中无法随便进个函数都要检查栈,更不能预先知道哪个函数入栈最深,所以只能放老鼠夹了.
技术讨论请发帖 , 需要我回复请点左下的 < 回复 > 让系统通知我 . 本人不通过其他方式返回任何参数.
回复 支持 反对

使用道具 举报

  离线 

14

主题

173

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
255
金钱
255
注册时间
2011-10-14
在线时间
1 小时
发表于 2013-11-17 16:20:36 | 显示全部楼层
回复【30楼】xiaoyan:
---------------------------------
谢谢你的解释,我这边看到是与你描述相同的结果
回复 支持 反对

使用道具 举报

  离线 

14

主题

173

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
255
金钱
255
注册时间
2011-10-14
在线时间
1 小时
发表于 2013-11-17 16:48:05 | 显示全部楼层
有些个人理解提出来,请高手指点下:
对STM32RBT6而言
1. 0x20000000 指向的SRAM的物理地址
2. 0x08000000-0x0801FFFF是内部Flash的物理地址
3. 刚刚提到的LR(R14)以及PC(R15)指向的地址如0x0800027E,看起来是指向内部FLASH,应该是指向 Code段的指令吧?
回复 支持 反对

使用道具 举报

  离线 

38

主题

2067

帖子

6

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3263
金钱
3263
注册时间
2012-1-16
在线时间
36 小时
发表于 2013-11-17 20:16:23 | 显示全部楼层
回复【33楼】shihantu:
---------------------------------
当某个函数调用这个栈容量检查的时候,可以知道,自己有没有溢出。如果我自己发现没有超过栈容量的话,但是还是溢出了,那就可以知道是“儿子”的问题了,这个信息还是比较有用吧。你说的"老鼠夹"是指这个么?
站在巨人的肩膀上不断的前进。。。
回复 支持 反对

使用道具 举报

  离线 

38

主题

2067

帖子

6

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3263
金钱
3263
注册时间
2012-1-16
在线时间
36 小时
发表于 2013-11-17 20:18:12 | 显示全部楼层
回复【35楼】smithlin:
---------------------------------
是的
站在巨人的肩膀上不断的前进。。。
回复 支持 反对

使用道具 举报

  离线 

500

主题

9万

帖子

31

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
129741
金钱
129741
注册时间
2010-12-1
在线时间
1169 小时
 楼主| 发表于 2013-11-17 22:11:24 | 显示全部楼层
回复【38楼】styleno1:
---------------------------------
那是分配到哪里?栈?
内存=堆+栈。
非堆即栈。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

  离线 

2

主题

1405

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1953
金钱
1953
注册时间
2010-12-16
在线时间
124 小时
发表于 2013-11-17 22:13:14 | 显示全部楼层
回复【36楼】xiaoyan:
---------------------------------
不一定是自己可以掌控的函数出现栈溢出,更麻烦的是,不一定能抓到现行.

比如18L的代码,如果是栈检查函数的printf里,栈稍微溢出几个字节,但这几个字节是不用的,或者是某些非指针数据,的话,在读取栈指针时不会发现溢出,在printf时不会触发hardfault,但退出函数后运行其他代码,可能就<发现>某个静态变量被莫名修改了.

注意,这个<发现>在这个帖的实验里可能是由于刻意跟踪发现的,但有时并不容易发现,比如它破坏了UART的发送FIFO,结果就仅仅发现几个误码,但又以为是线路问题忽略而过.在大型系统里,要如此纤细的查找错误是很麻烦的,特别是在没有完善的检查机制的系统里.
技术讨论请发帖 , 需要我回复请点左下的 < 回复 > 让系统通知我 . 本人不通过其他方式返回任何参数.
回复 支持 反对

使用道具 举报

  离线 

2

主题

1405

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1953
金钱
1953
注册时间
2010-12-16
在线时间
124 小时
发表于 2013-11-17 22:14:44 | 显示全部楼层
忘了说,"老鼠夹"就是我不一定盯着老鼠踩夹子,但老鼠踩了即使没抓着也看到夹子合上了.
技术讨论请发帖 , 需要我回复请点左下的 < 回复 > 让系统通知我 . 本人不通过其他方式返回任何参数.
回复 支持 反对

使用道具 举报

  离线 

2

主题

1405

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1953
金钱
1953
注册时间
2010-12-16
在线时间
124 小时
发表于 2013-11-17 22:15:30 | 显示全部楼层
回复【39楼】正点原子:
---------------------------------
静态变量不在堆和栈里.
技术讨论请发帖 , 需要我回复请点左下的 < 回复 > 让系统通知我 . 本人不通过其他方式返回任何参数.
回复 支持 反对

使用道具 举报

  离线 

2

主题

1405

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1953
金钱
1953
注册时间
2010-12-16
在线时间
124 小时
发表于 2013-11-17 22:17:55 | 显示全部楼层
全局动态变量在堆里分配,局部动态变量在栈里分配.

原子可以在例程里开一些堆空间,无需使用,然后检查heap和stack的空间地址.
技术讨论请发帖 , 需要我回复请点左下的 < 回复 > 让系统通知我 . 本人不通过其他方式返回任何参数.
回复 支持 反对

使用道具 举报

  离线 

500

主题

9万

帖子

31

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
129741
金钱
129741
注册时间
2010-12-1
在线时间
1169 小时
 楼主| 发表于 2013-11-17 22:42:25 | 显示全部楼层
回复【42楼】 shihantu :
---------------------------------
那就奇怪了,我测试,静态变量,也是在堆区的。
如下图:


可以看到,stack_dir和cpu_endian,就躺在atest和atest1之间呢....
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

  离线 

2

主题

1405

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1953
金钱
1953
注册时间
2010-12-16
在线时间
124 小时
发表于 2013-11-17 23:02:15 | 显示全部楼层
回复【44楼】正点原子:
---------------------------------
0x20000000开始的位置,链接器首先放置了静态变量.43L的几个变量都是全局静态变量.

原子可以写一个无穷迭代的函数,小心观察全局变量被栈破坏的过程,而且不进入hardfault.
技术讨论请发帖 , 需要我回复请点左下的 < 回复 > 让系统通知我 . 本人不通过其他方式返回任何参数.
回复 支持 反对

使用道具 举报

  离线 

54

主题

537

帖子

0

精华

高级会员

Rank: 4

积分
797
金钱
797
注册时间
2012-2-27
在线时间
7 小时
发表于 2013-11-17 23:12:05 | 显示全部楼层
回复【楼主位】正点原子:
---------------------------------
STM32属于小端模式,简单的说,比如u32 temp=0X12345678;
假设temp地址在0X2000 0010.
那么在内存里面,存放就变成了:
地址              |            HEX         |
0X2000 0010  |  78   56   43  12  

原子哥,这里没有看明白,我理解的是不是应该这样
地址              |            HEX         |
0X2000 0010          78     
0X2000 0011          56
0X2000 0012          34
0X2000 0013          12
回复 支持 反对

使用道具 举报

  离线 

500

主题

9万

帖子

31

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
129741
金钱
129741
注册时间
2010-12-1
在线时间
1169 小时
 楼主| 发表于 2013-11-17 23:14:15 | 显示全部楼层
回复【46楼】wwjdwy:
---------------------------------
就是这样。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

  离线 

2

主题

1405

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1953
金钱
1953
注册时间
2010-12-16
在线时间
124 小时
发表于 2013-11-17 23:20:41 | 显示全部楼层
这个是LZ代码改的测试代码,修改了堆和栈的长度,main函数增加了初始化串口缓冲的操作,和调用了一个无限迭代的函数.

堆栈位置在<test.map>line:431-435和line:779-780.

F11执行函数Iteration,会看到内存区会不断被Iteration的地址覆盖,直到破坏所有静态变量空间.

如果静态变量在栈区,按照栈先进后出的机制,应该不会被破坏.

当然,如果链接器是先放栈,再放堆,最后放静态变量,就做不了这个实验了.






ST32堆栈增长方向及大小端测试代码.zip

463.4 KB, 下载次数: 2633

技术讨论请发帖 , 需要我回复请点左下的 < 回复 > 让系统通知我 . 本人不通过其他方式返回任何参数.
回复 支持 反对

使用道具 举报

  离线 

500

主题

9万

帖子

31

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
129741
金钱
129741
注册时间
2010-12-1
在线时间
1169 小时
 楼主| 发表于 2013-11-17 23:26:46 | 显示全部楼层

回复【45楼】 shihantu :
---------------------------------
哦哦,看来我搞错一个很大的概念了。
从百度文库拷贝的:

一、内存基本构成 
可编程内存在基本上分为这样的几大部分:静态存储区、堆区和栈区。他们的功能不同,对他们使用方式也就不同。 

静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。 

栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 

堆区:亦称动态内存分配。程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在适当的时候用free或delete释放内存。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存。 但是,良好的编程习惯是:如果某动态内存不再使用,需要将其释放掉,否则,我们认为发生了内存泄漏现象。


按照这个说法,我在.s文件里面设置了:

Heap_Size       EQU     0x00000000

也就是,没有任何动态内存分配。
这样,内存=静态存储区+栈区了。
不存在堆!!!
因为我没有用malloc来动态分配内存。
因此,前面提到的一切堆区,其实就是静态存储区。

我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

  离线 

500

主题

9万

帖子

31

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
129741
金钱
129741
注册时间
2010-12-1
在线时间
1169 小时
 楼主| 发表于 2013-11-17 23:51:56 | 显示全部楼层
另外,经过测试,确实是这样。
STM32的内存分配,应该分为两种情况。
1,使用了系统的malloc。
2,未使用系统的malloc。

第一种情况(使用malloc):
STM32的内存分配规律:
从0X20000000开始依次为:静态存储区+堆区+栈区

第二种情况(不使用malloc):
STM32的内存分配规律:
从0X20000000开始依次为:静态存储区+栈区

第二种情况不存在堆区。
所以,一般对于我们开发板例程,实际上,没有所谓堆区的概念,而仅仅是:静态存储区+栈区。
无论哪种情况,所有的全局变量,包括静态变量之类的,全部存储在静态存储区。
紧跟静态存储区之后的,是堆区(如没用到malloc,则没有该区),之后是栈区。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则




关闭

"原子哥”推荐上一条 /1 下一条

QQ|联系我们|手机版|官方淘宝店|新浪微博|微信公众平台|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2018-7-20 16:35

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表
/* */