ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

初步理解c语言栈的运行机理、代码段分区

2021-05-30 15:01:21  阅读:183  来源: 互联网

标签:10 函数 int 分区 代码段 子函数 数组 空间 机理


栈区:

  栈(stack):是一种先进后出的内存结构,所有的局部变量,函数的形参都是由编译器自动放出栈中,当一个自动变量超出其作用域时,自动从栈中弹出。出入栈是由C语言编译器自动分配释放。

  栈大小(stacksize):通常可以配置编译器或通过改写链接文件调整栈空间大小。

  栈溢出:当栈空间已满,还继续往栈内压变量,会导致栈溢出,通常表现为程序非预期运行(部分RAM数据乱掉,严重情况下会导致程序跑飞)。

 

基于飞思卡尔9S12系列 16bit MCU理解栈的运行机理

1、建立以下示例代码片上仿真工程(9S12D64为例说明)

 1 #include <hidef.h>      /* common defines and macros */
 2 #include "derivative.h"      /* derivative-specific definitions */
 3 
 4 static int StaticTestNotInit[10]; /* 未初始化的静态数组 */
 5 int TestNotInit[10];  /* 未初始化的全局变量数组 */
 6 int Test[] = {0x10,0x20}; /* 赋初值的全局变量数组 */
 7 
 8 int TestFunction(int a, int b) {
 9   static int TempVar[10]; /* 子函数静态数组 */
10   static int Index = 0;
11   
12   int TestTempList[8] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};  /* 子函数局部变量数组 */
13   
14   int ret = a + b + TestTempList[Index];
15   TempVar[Index++] = ret;
16   if(Index >= (sizeof(TempVar)/sizeof(int))) Index = 0;
17   
18   return ret;
19 }
20 
21 void main(void) {
22   /* put your own code here */
23   int i = 0;  /* 主函数临时变量 */
24   for(i = 0; i < sizeof(StaticTestNotInit)/sizeof(int);i++)
25     StaticTestNotInit[i] = 0x01;
26     
27   for(i = 0; i < sizeof(TestNotInit)/sizeof(int);i++)
28     TestNotInit[i] = 0x02;
29     
30   for(i = 0; i < sizeof(Test)/sizeof(int);i++)
31     Test[i] = 0x03;
32     
33   i = TestFunction(Test[0],Test[1]);
34   i = TestFunction(TestNotInit[0],StaticTestNotInit[1]);
35 
36     EnableInterrupts;
37 
38 
39   for(;;) {
40     _FEED_COP(); /* feeds the dog */
41   } /* loop forever */
42   /* please make sure that you never leave main */
43 }

2、编译后打开map文件:

2.1 栈区ram划分

 

栈大小(stacksize),工程默认为0x100,地址从0x400-0x4FF,可在链接文件prm中修改,如下图所示,修改为0x200后再次编译查看map文件。

 

 

 

 

 

 栈空间调整为0x200,地址范围为:0x400-0x5FF

2.2 不同位置定义的变量内存分配

 

 

 

 

 如上图所示:

第4行定义的未初始化的静态数组 StaticTestNotInit[10] ,分配在.bss数据段,地址空间为0x606-0x619,共20字节(16位机的int宽度为2字节)

第5行定义的未初始化全局变量数组 TestNotInit[10] ,分配在.common数据段,地址空间为0x62E-0x641,同样为20字节

第6行定义的赋初值的全局变量数组 Test[2] ,分配在.data数据段,地址空间为0x600-0x603,共4字节

第9行、10行定义的子函数静态变量TempVar[10],Index,分配在.bss数据段,地址空间分别为0x61A-0x62D和0x604-0x605;

其他变量(main函数内的i,TestFaunction函数内部的TestTempList[8],ret等),均在运行时分配在栈空间内。

3、仿真调试

3.1 mian函数运行开始时的memory分布

  如下图所示,MCU上电运行startup函数,调用main函数接口时,将startup函数的下一条指令压栈,即栈区的0x5FD-0x5FF地址空间(0x00C00B,特别说明:9S12系列单片机全局地址为24bit)。

.data段数据直接被startup函数填充为对应初值,.bss及.common数据段则被startup函数填充为0

(startup函数的实现,此处暂不讨论,通常应用也无需关注,当需要做bootloader时再进行深入探究)

 

 

   单步运行至24行代码时,此时第23行定义的局部变量i被分配到栈空间,并被附初值0

 

 

   继续运行至27行代码,可以看到StaticTestNotInit数组所有值均被更改为0x01

 

 

    继续运行至30行代码,可以看到TestNotInit数组所有值均被更改为0x02

 

 

     继续运行至33行代码,可以看到Test数组所有值均被更改为0x03

 

  进入子函数,此时先将形参a压入栈空间(0x5F9-0x5FA),主函数的下一条指令地址(0x3C809C)被压入栈空间(0x5F6-0x5F8)

 

 

   单步运行,此时可以看到,先为局部变量数组TestTempList[8]以及ret分配了栈空间,然后在将第二个形参压入栈空间(0x5E2-0x5E3),此处压栈顺序与编译器有关

 

   继续单步,可以看到初步对TestTempList赋初值,然后计算出ret的结果

 

   当子函数执行完毕后,返回主函数时,子函数分配的栈空间自动回收

 

 

基于瑞萨RH850系列 32bit MCU理解栈的运行机理(暂略,后期补充)

 

 

总结

综上所述,子函数调用会将主调函数的下一条指令地址、子函数形参压栈,并在栈空间中为子函数分配所需的所有局部变量(按作用域进行分配和回收)。

所以,工程应用时,可根据map文件中的函数调用关系,查看各个调用链级的局部变量、形参数量进行STACKSIZE预估,并按照1.5-2倍进行空间预留,才能有效保证栈空间的安全性

PS:函数指针部分编译器无法绘制调用关系,并且MCU运行工况的高复杂性,中断调用等均需消耗栈空间,所以为了保证安全性,需进行空间预留

栈溢出判断:可在项目进行性能调优阶段,增加栈空间监测,采用高精度定时器,实时判断栈空间80-90%位置的数据是否被改变,通过IO翻转输出(示波器捕捉),用于确保栈空间的安全性。

 

标签:10,函数,int,分区,代码段,子函数,数组,空间,机理
来源: https://www.cnblogs.com/MrZhang1700/p/14827994.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有