一、从stm32引入理解
1、MDK编译log:
Program Size: Code=72072 RO-data=6864 RW-data=1316 ZI-data=8684
Code 是存储程序代码的;(删掉代码中未使用的函数等以节省flash空间)
RO-data 是存储const常量和已初始化的字符串等;(类比于文字常量区)
RW-data 是存储可读写的初始化值不为0的全局变量和静态变量;(类比于其他平台的data段,数据保持在bin文件中)
ZI-data 是存储未初始化或者初始化值为0的全局变量和静态变量;未初始化的变量BIN文件只需要记录其大小,等程序运行时在RAM开辟空间。(类比于其他平台的bss段(block started by symbol),data段和bss段又合称为数据区)
=Heap堆= new, malloc手动动态分配的内存,需要手动delete,free释放。如果不释放,只有等程序运行正常结束时,由操作系统回收,理论上无限大。
对大内存的操作,需要放堆区。如C++:
student stu[4*1024*1024] ;会出问题,应改为:student *stu=new stu[4*1024*1024]; 释放:delete[] stu;
=stack栈 =程序运行的场景信息(函数的参数,返回值,局部变量)。由操作系统自动分配/释放,空间有限。程序在编译期对变量和函数内存分配都在栈上进行,且程序运行过程中函数调用时参数的传递也在栈上进行。
FLASH大小 = Code + RO-data +RW-data = BIN大小
RAM大小 = RW-data + ZI-data+堆+栈
在stm32的启动.s文件里面,可以根据需要将栈大小设置得大点,堆栈的内存占用是在RAM分配给 RW-data + ZI-data后的地址开始分配的。
=====bss段和data段相关:
bss 段: block started by symbol;存放未初始化的全局变量的内存区域;属于静态内存分配。并不给该段的数据分配空间,只是在bin中记录数据所需空间的大小。
data段:存放已初始化的全局变量;属于静态分配,数据保存在目标文件中。
===== stm32f10x的堆栈设置:
startup_stm32f10x_hd.s
======stm32f10x的falsh地址和Ram地址设置:
stm32f10x.h
=========测试验证方法:打印变量的地址
{
char c = 'a';
printf("c address is = %p\n", &c);
char *p1 = &c;
printf("c address is = 0X%X\n", p1);
}
1.定义多个全局变量和静态变量,打印查证其地址是否从0x20000000开始,证明全局/静态变量就是从内存首地址开始的。
2.定义const只读变量或者字符串,打印查证其地址是否从0x08000000开头,处于flash区域,说明RO-DATA的确不占用RAM空间
3.定义一个局部变量,打印出其地址,发现是以0x20000000开头的,说明局部变量是处于栈区的。
==============================
二、堆栈相关:
1、内存分栈内存、堆内存、全局变量和静态变量内存。
栈内存类似于栈,一般最多只有1-2M大小,函数内部的一般方式声明的变量都是放在函数的栈内存中,所以太大的数组会栈溢出。全局变量内存一般没有上限,堆内存也没有。所以你可以用全局变量或者动态分配内存(堆内存)。
2、堆栈溢出一般是由什么原因导致的?
堆栈溢出一般是循环的递归调用导致的;使用大数据结构的局部变量,也可能导致堆栈溢出;已分配的堆内存未释放导致的内存泄漏,会造成系统内存浪费,系统运行速度减慢甚至崩溃。
3、请问下面程序有什么错误?
{
int a[60][250][1000],i,j,k;
for(k=0;k<1000;k++)
for(j=0;j<250;j++)
for(i=0;i<60;i++)
a[i][j][k]=0;
}
答:循环语句内外换一下
(编译的时候没错,运行的时候出错,这个数组太大,如果放在栈中,还是会溢出,要作为全局变量)
==============================
三、存储相关
分析下面的代码:
1、
{
char s[], char *p2; //p2,s 都在栈上
char *p3 = “123456”; //p3在栈上,123456\0 在常量区
char *p1;
p1= (malloc)(10); //p1在栈上,10字节的内存在堆上
strcpy(p1, “123456”);
//123456\0 在常量区,编译器可能将它与p3所指向的“123456”优化成—个地方。
free(p1); //释放掉10字节的堆空间,p1还在,要进行NULL赋值
p1=NULL;
}
2、
{
char *a = "hello";
char *b = "hello";
if(a==b)
printf("YES");
else
printf("NO");
}
常量字符串"hello"位于静态存储区,它在程序生命期内恒定不变。如果编译器优化的话,会有可能a和b同时指向同一个hello的,则它们地址相同。如果编译器没有优化,那么就是两个不同的地址,则不同。对VC,是相同。
3、写出并解释下列代码的输出结果:
{
char str1[] = "abc";
char str2[] = "abc";
const char str3[] = "abc";
const char str4[] = "abc";
const char *str5 = "abc";
const char *str6 = "abc";
char *str7 = "abc";
char *str8 = "abc";
cout << ( str1 == str2 ) << endl;
cout << ( str3 == str4 ) << endl;
cout << ( str5 == str6 ) << endl;
cout << ( str7 == str8 ) << endl;
}
解答:结果是:0 0 1 1
str1,str2,str3,str4是数组变量,它们有各自的内存空间;
而str5,str6,str7,str8是指针,它们指向相同的常量区域,编译器优化节省内存。
4、指针和数组的空间分配
分为两种情况:
第一,如果是全局的和静态的
char *p = “hello”;
这是定义了一个指针,指向rodata section里面的“hello”,可以被编译器放到字符串池。在汇编里面的关键字为.ltorg。意思就是在字符串池里的字符串是可以共享的,这也是编译器优化的一个措施。
char a[] = “hello”;
这是定义了一个数组,分配在可写数据块,不会被放到字符串池。
第二,如果是局部的
char *p = “hello”;
这是定义了一个指针,指向rodata section里面的“hello”,可以被编译器放到字符串池。在汇编里面的关键字为.ltorg。意思就是在字符串池里的字符串是可以共享的,这也是编译器优化的一个措施。另外,在函数中可以返回它的地址,也就是说,指针是局部变量,但是它指向的内容是全局的。
char a[] = “hello”;
这是定义了一个数组,分配在堆栈上,初始化由编译器进行。(短的时候直接用指令填充,长的时候就从全局字符串表拷贝),不会被放到字符串池(同样如前,可能会从字符串池中拷贝过来)。注意不应该返回它的地址。