最近,有时间维护自己以前写的stm32程序,想到开始写的时候写了大量的局部变量,程序会有问题。后来有人指点栈溢出,将大量局部变量变成了全局变量就好了,当时也没弄明白怎么回事,今天有空了解下来龙去脉。
先了解下什么是栈。
c/c++编译的程序占用的内存分为以下几类:
1、栈区(stack):存放函数的参数,局部变量值。编译器自动分配释放。
2、堆区(heap):程序员自己申请的内存,用malloc或new申请的内存,在适当的时候程序员自己释放,不然程序在最后才释放掉。此种内存分配也称为动态内存分配,良好的编程习惯是,某动态内存不再使用时,需要将其释放掉,不然我们认为发生了内存泄露现象。
3、全局区、静态区(static):存放全局变量和静态变量。初始化的全局变量和静态变量放在一块,未初始化的全局变量和静态变量放在另一块。程序结束后系统释放。
4、文字常量区:常量字符串就放在这里的。程序结束后系统释放。
5、程序代码区:存放函数体的二进制代码。
在stm32启动文件.s中有设置栈的大小。Stack_Size EQU 0x00000400。所有在函数中不能放入过多的全局变量,尤其是大的数组。我之前正是犯的这个错误。
在.map文件末尾有image部分有:
Image component sizes
Code (inc. data) RO Data RW Data ZI Data Debug Object Name
360 32 0 0 40 2538 can.o
432 46 0 4 4020 3716 can1infifo.o
76 12 0 4 4020 1655 can1outfifo.o
11284 1666 0 325 24150 21505 color.o
144 22 0 2 0 6660 delay.o
532 40 12 0 11024 4240 fifo.o
250 28 0 9 164 4343 flash.o
13120 1222 30 148 11051 52981 main_70.o
156 32 0 0 0 4205 misc.o
56 16 0 11 0 2313 noteinfofifo.o
56 22 236 0 1536 872 startup_stm32f10x_md.o
504 24 0 0 0 18428 stm32f10x_adc.o
1330 12 0 0 0 14137 stm32f10x_can.o
486 30 0 0 0 14418 stm32f10x_dma.o
184 16 0 0 0 4686 stm32f10x_exti.o
352 32 0 0 0 11898 stm32f10x_flash.o
458 10 0 0 0 20624 stm32f10x_gpio.o
444 66 0 20 0 216027 stm32f10x_rcc.o
468 28 0 0 0 26887 stm32f10x_tim.o
302 4 0 0 0 11965 stm32f10x_usart.o
320 24 20 0 0 2673 system_stm32f10x.o
260 24 0 2 0 264622 uart.o
----------------------------------------------------------------------
31602 3408 330 532 56008 711393 Object Totals
0 0 32 0 0 0 (incl. Generated)
28 0 0 7 3 0 (incl. Padding)
----------------------------------------------------------------------
Code (inc. data) RO Data RW Data ZI Data Debug Library Member Name
90 0 0 0 0 0 __dczerorl2.o
8 0 0 0 0 68 __main.o
52 8 0 0 0 0 __scatter.o
28 0 0 0 0 0 __scatter_zi.o
12 0 0 0 0 72 exit.o
6 0 0 0 0 152 heapauxi.o
0 0 0 0 0 0 indicate_semi.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
138 0 0 0 0 68 rt_memcpy_v6.o
100 0 0 0 0 80 rt_memcpy_w.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
12 4 0 0 0 68 sys_exit.o
74 0 0 0 0 80 sys_stackheap_outer.o
2 0 0 0 0 68 use_no_semi.o
450 8 0 0 0 236 faddsub_clz.o
388 76 0 0 0 96 fdiv.o
54 4 0 0 0 84 ffix.o
86 0 0 0 0 136 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
----------------------------------------------------------------------
1964 112 0 0 100 1512 Library Totals
10 0 0 0 4 0 (incl. Padding)
----------------------------------------------------------------------
Code (inc. data) RO Data RW Data ZI Data Debug Library Name
568 16 0 0 96 724 c_w.l
1386 96 0 0 0 788 fz_ws.l
----------------------------------------------------------------------
1964 112 0 0 100 1512 Library Totals
----------------------------------------------------------------------
==============================================================================
Code (inc. data) RO Data RW Data ZI Data Debug
33566 3520 330 532 56108 703805 Grand Totals
33566 3520 330 40 56108 703805 ELF Image Totals (compressed)
33566 3520 330 40 0 0 ROM Totals
==============================================================================
Total RO Size (Code + RO Data) 33896 ( 33.10kB)
Total RW Size (RW Data + ZI Data) 56640 ( 55.31kB)
Total ROM Size (Code + RO Data + RW Data) 33936 ( 33.14kB)
==============================================================================
通过这个文件就能分析整个内存是怎么被占用的,具体到每一个文件占用多少。
stm32内存分配应该分为两种情况:
第一种情况(使用malloc):
stm32的内存分配规律:
从0x2000 0000开始依次为:静态存储区+堆区+栈区
第二种情况(未使用malloc):
stm32的内存分配规律:
从0x2000 0000开始依次为:静态存储区+栈区
从分配内存空间看:
全局变量、静态全局变量、静态局部变量都在静态存储区分配空间,而局部变量在栈分配空间。
从作用域看:
全局变量具有全局作用域。全局变量只需在一个源文件中定义,就可以作用于所有的源文件。当然,其他不包括全局变量定义的源文件需要用extern关键字再次声明这个全局变量。
静态全局变量也具有全局作用域,他与全局变量的区别在于如果程序包含多个文件的话,他作用于定义它的文件里,不能作用到其他文件里,即被static关键字修饰过的变量具有文件作用域。这样即使两个不同的源文件都定义了相同的静态全局变量,他们也是不同的变量。
局部变量也只有局部作用域,他是自动对象,他在程序运行期间不是一直存在,而是只在函数执行期间存在,函数的一次调用结束后,变量就被撤销,其所占用的内存也被收回。
静态局部变量具有局部作用域。它只被初始化一次,自从第一次初始化直到程序与你新内阁结束都一直存在,他和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见。
从以上分析可以看出,把局部变量改变为静态变量后是改变了他的存储方式,即改变了他的生存期。把全局变量改变为静态变量后是改变了他的作用域,限制了他的使用范围,因此static这个说明符在不同的地方起的作用是不同的。
TIPS:
1、若全局变量仅在单个文件中访问,则可以将这个变量修改为静态全局变量。
2、若全局变量仅在单个函数中使用,则可以将这个变量修改为该函数的静态局部变量。
3、全局变量、静态局部变量、静态全局变量都存放在静态数据存储区。
4、函数中必须要使用static变量的情况:当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。