素材来源:https://blog.csdn.net/qq_34430371/article/details/125903384
整理:技术让梦想更伟大 | 李肖遥
笔者来说说看法,经过试验得出的结果,以ARMCC、IAR以及GCC为例
以一个例程来分析,led.c 最简单的
u32 LEDValue1 = 0XFFFF;
const u32 LEDValue2=0XFFFF;
u32 LEDValue3[4];
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);//使能GPIOF时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_CRC, ENABLE);
//GPIOF9,F10初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12 |GPIO_Pin_13| GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//上拉
GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化
GPIO_Write(GPIOD,LEDValue1); //以下代码都是为了测试
GPIO_Write(GPIOE,LEDValue2);
LEDValue3[0]=0XFFFF;
GPIO_Write(GPIOE,LEDValue3[0]);
}
void LedRun()
{
GPIO_ResetBits(GPIOD,GPIO_Pin_5);
GPIO_SetBits(GPIOD,GPIO_Pin_5);
}
1234567891011121314151617181920212223242526272829303132
然后打开生成的map文件,可以看到具体编译好的信息,很方便分析单个.o文件所占用的size信息,比如该文件led.c
从上面Map信息里面看到Led.o的size情况:
所以如果算法单独使用了一个.o文件,在armcc下,很容易分析出数据的空间使用大小。但是栈的空间+堆的空间没有统计到,
堆是运行态的,静态编译出来的无法统计到,需要具体的情况具体分析,单独去看malloc这种,或者自己内存管理的空间申请。
至于栈的使用空间,编译阶段可能不知道,因为编译阶段不知道调用关系,而链接的时候则由链接器将多个.o文件组织起来,所以可以知道调用关系,
接着来分析一下,为啥LED_Init函数的栈使用了24Byte空间。
大家都知道,数据的运算以及函数的调用,都会用到寄存器,而用寄存器之前需要保存寄存器,所以栈主要是用来保存该函数用到的寄存器,来看一下汇编,很容易就明白了。
push的时候,都是4字节对齐的(寄存器都是32位的),所以总共push了6个寄存器,总共24Byte。
push {r2-r6,r14}
1
从上文可以看到LED.o共使用了156Byte,
从map文件中来看,两个函数分别是116Byte + 24Byte,共140Byte,由上文可知,共156Byte,那么16Byte就是上文中的inc.data,就是Code中用到了一些数据,这些数据无法直接访问,需要开辟一块单元来存储这些数据地址,然后才可以加载。如下面第二张图所示,比如0x40021000,很明显这个就不是Code的地址或者RAM的地址,就是一个外设地址(GPIOE),根据STM32的手册可以得知(下面图三)。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上文中RW Data 或者ZI Data 符合预期,但是RO Data 很奇怪是0,因为我们本身定义了一个const的类型的数据,但是统计竟然是0,
u32 LEDValue1 = 0XFFFF;
const u32 LEDValue2=0XFFFF;
u32 LEDValue3[4];
123
这个需要从汇编入手,编译器也不傻,定义一个const 类型的数据,编译器会就生成一个RO data吗,不一定,比如本文这个,编译器直接将0xFFFF 编译到指令中,而不是从变量中加载数据,这个需要从汇编中看。
如何才能产生一个RO data呢?如果引用到变量的地址,那么肯定会产生一个RO data,因为需要分配变量地址。例如下文中这样。
u32 * data_p = (u32*)&LEDValue2;
*data_p = *data_p +1;
GPIO_Write(GPIOE,LEDValue2);
123
然后分析map文件,可以看到RO data Size 为4,led.o中有了RO的变量以及地址,也可以看到ro data的地址不是在sram,而是在flash中(ROM)中,最后汇编也为const 变量申请了存储空间(LEDValue2)
对于B同学的观点,我基本 都是赞同的。补充一下:就是所需要的内存,可能还需要加上Code所需要的空间(如果有这种场景的话,在内存中允许代码)
对于栈是动态的理解,我的想法也是栈是动态变化的
map文件是看不到局部变量的,原因有两点,
map文件中看到的 全局变量 或者局部静态变量。
对于C同学的观点,很多我都有不同的意见,
上面那个LedRun函数可能没有局部变量,那我们来加一个局部变量来看看,例如下面的代码,如果包括局部变量,那么函数的size一定会超过100,毕竟还有指令的size,实际编出来 的map文件分析,看到函数大小为64,分析汇编代码,指令数也是64,可以得出结论,函数的大小是不包括局部变量的。
void LedRun()
{
u16 LEDData[100]={123};
u8 i=0;
for(i=0;i<100;i++)
GPIO_Write(GPIOE,LEDData[i]); //测试,可能没意义
GPIO_ResetBits(GPIOD,GPIO_Pin_5);
GPIO_SetBits(GPIOD,GPIO_Pin_5);
}
123456789
o文件的大小 包括了 code、data(RO RW ZI)的大小,也没包括局部变量的大小。
但是函数本身使用的局部变量空间是可以统计出来的,刚刚也看到了,通过链接器生成的信息。
216 = (1002)+ Push(44 寄存器r4 r5 r6 r14)
举例说明,本程序的栈空间是0x400,还是刚刚的程序,同样可以编译过,没有报任何错误,还统计出来栈的使用情况(2064> 0x400(1024))。
void LedRun()
{
u16 LEDData[0x400]={123};
u16 i=0;
for(i=0;i<0x400;i++)
GPIO_Write(GPIOE,LEDData[i]); //测试,可能没意义
GPIO_ResetBits(GPIOD,GPIO_Pin_5);
GPIO_SetBits(GPIOD,GPIO_Pin_5);
}
123456789
在这里插入图片描述
以上就是笔者分析的一些情况,有不同分意见可以分享评论。后面简单以IAR以及arm-gcc 分析,看看是否有所不同。
附录:
版权声明:本文来源网络,免费传达知识,版权归原作者所有。如涉及作品版权问题,请联系我进行删除。
‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧ END ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有