函数体内的局部变量是位于栈中。很多人回答时,总是带上堆,回答是在堆栈中,其实是他们没有理解“堆和栈”的意义。
局部变量位于栈外面,程序可能可以工作,但是肯定会有问题。
我们首先看启动文件,了解堆和栈的大小,重要的代码部分如下:
; Amount of memory (in bytes) allocated for Stack
; Tailor this value to your application needs
; <h> Stack Configuration
; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Stack_Size EQU 0x00000100
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
; <h> Heap Configuration
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Heap_Size EQU 0x00000008
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
从上面代码,我们可以看出:
Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>,意思是栈空间按照8字节对齐;于是它定义 “STACK, NOINIT, READWRITE, ALIGN=3”,即按照2^3=8字节对齐;
“Stack_Size EQU 0x00000100”表示栈的大小为256个字节;
Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>,意思是堆空间按照8字节对齐;于是它定义 “STACK, NOINIT, READWRITE, ALIGN=3”,即按照2^3=8字节对齐;
“Heap_Size EQU 0x00000008”表示栈的大小为8个字节;
在非系统的STM32程序中,也就是裸机的STM32程序中,这个堆几乎用不到。即使我们用到动态内存分配,也不会用到。因为我们通常会在RAM中划分出一块存储区用作内存分配,所以还是用不到KEIL划分的堆,因此,我们可以将这个堆设置为8,即堆的大小为8个字节。
下面分析函数体内的声明的数组是位于栈中,代码如下:
#include "stm32f10x.h"
#include "USART1.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "stdlib.h"
#include <string.h>
u32 STACK_TOP;
void Array_Test1(char *src)
{
STACK_TOP=__get_MSP();//读取栈指针
printf(" STACK_TOP3: 0x%x",STACK_TOP);
*src='A';
}
void Array_Test(void)
{
char array[40];//占用内存为40个字节
STACK_TOP=__get_MSP();//读取栈指针
printf(" STACK_TOP2: 0x%x",STACK_TOP);
Array_Test1(array);
STACK_TOP=__get_MSP();//读取栈指针
printf(" STACK_TOP4: 0x%x",STACK_TOP);
printf(" array address: 0x%x",array);
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
USART1_Serial_Interface_Enable(115200);
printf("\r\nCPU reset\r\n");
STACK_TOP=__get_MSP();//读取栈指针
printf("\r\nSTACK_TOP1: 0x%x",STACK_TOP);
while(1)
{
Array_Test();
STACK_TOP=__get_MSP();//读取栈指针
printf("\r\nSTACK_TOP1: 0x%x",STACK_TOP);
}
}
编译成功后,打开“*.map”文件,找到下面的信息:
Symbol Name Value Ov Type Size Object(Section)
.data 0x20000000 Section 4 main.o(.data)
.data 0x20000004 Section 4 usart1.o(.data)
.data 0x20000008 Section 20 stm32f10x_rcc.o(.data)
APBAHBPrescTable 0x20000008 Data 16 stm32f10x_rcc.o(.data)
ADCPrescTable 0x20000018 Data 4 stm32f10x_rcc.o(.data)
.data 0x2000001c Section 20 system_stm32f10x.o(.data)
.bss 0x20000030 Section 96 libspace.o(.bss)
HEAP 0x20000090 Section 8 startup_stm32f10x_hd.o(HEAP)
Heap_Mem 0x20000090 Data 8 startup_stm32f10x_hd.o(HEAP)
STACK 0x20000098 Section 256 startup_stm32f10x_hd.o(STACK)
Stack_Mem 0x20000098 Data 256 startup_stm32f10x_hd.o(STACK)
__initial_sp 0x20000198 Data 0 startup_stm32f10x_hd.o(STACK)
从上面的“*.map”文件,我们可以看出:
Heap的起始地址为0x20000090,有8个字节的存储空间;
STACK的起始地址为0x20000198,结束地址为0x20000098,因为栈是向下生成的,共计有256个字节的存储空间;
通过仿真我们得到下面的信息:
可以看到array[40]的首地址为0x2000016C,由于STACK的起始地址为0x20000198,结束地址为0x20000098,因此函数体内的数组是位于栈中。如果函数体内的暂态变量比栈空间大,程序一旦运行到这个函数,就会引起死机。因此,不用认为编译都没有问题了,怎么会死机呢。
STACK TOP1: 0x20000198 STACK TOP2: 0x20000168 STACK TOP3: 0x20000160 STACK TOP4:0x20000168 array address: 0x2000016c
0x20000198-0x20000168-8=40
这里减去8,是因为进入Array_Test()函数,需要将PC指针等压入栈中,所以这里要减去8;
在Array_Test()中,声明数组如下:
char array[40];//占用内存为40个字节
通过比对,位于栈中的数组的字节数是正确的,同时数组的地址范围位于栈中。因此,函数体内的声明的局部变量是位于栈中的。