8 内存
8.1 内存四区
8.1.1 栈区(stack)
由编译器自动分配和释放,主要是存放函数参数的值,局部变量的值。
8.1.2 堆区(heap)
由程序员自己申请分配和释放,需要malloc()、calloc()、realloc()
函数来申请,用free()
函数来释放如果不释放,可能出现指针悬空/野指针。
函数不能返回指向栈区的指针,但是可以返回指向堆区的指针。
8.1.3 数据区(data)
变量标有static关键字,保存了静态变量。
- 初始化的全局变量和初始化的静态变量,在一块区域;
- 未初始化的全局变量和未初始化的静态变量,在一块区域,称作BSS(Block Started by Symbol:以符号开始的块);
- 静态变量的生命周期是整个源程序,而且只能被初始化一次,之后的初始化会被忽略。
(如果不初始化,数值数据将被默认初始化为0, 字符型数据默认初始化为NULL)。
整个数据区的数组,在程序结束后由系统统一销毁。
8.1.4 代码区(code)
用于存放编译后的可执行代码,二进制码,机器码。
8.2 堆和栈的比较
比较方面 |
栈 |
堆 |
管理方式 |
由系统自动管理,以执行函数为单位 |
由程序员手动控制 |
空间大小 |
空间大小编译时确定(参数+局部变量) |
具有全局性,总体无大小限制。 |
分配方式 |
函数执行,系统自动分配;函数结束,系统立即自动回收。 |
使用new/malloc()手动分配释放;使用delete/free()手动释放 |
优点 |
使用方便,不需要关心内存申请释放。 |
可以跨函数使用。 |
缺点 |
只能在函数内部使用。 |
容易造成内存泄露。 |
8.3 实例分析内存四区
#include <stdio.h>
#include <stdlib.h>
int ga = 0;
int gb = 0;
int gc = 0;
void Func1(){
int a = 1;
int b = 2;
int c = 3;
printf("Func:&a = %p\n",a);
printf("Func:&b = %p\n",b);
printf("Func:&c = %p\n",c);
}
void Func2(){}
void Func3(){}
int main(){
int a;
int b;
int c;
printf("&a = %p\n",&a);
printf("&b = %p\n",&b);
printf("&c = %p\n",&c);
int* p1 = malloc(sizeof(int));
int* p2 = malloc(sizeof(int));
int* p3 = malloc(sizeof(int));
printf("p1 =\t%p\n",p1);
printf("p2 =\t%p\n",p2);
printf("p3 =\t%p\n",p3);
printf("&ga =\t%p\n",&ga);
printf("&gb =\t%p\n",&gb);
printf("&gc =\t%p\n",&gc);
static int sa = 0;
static int sb = 0;
static int sc = 0;
printf("&sa =\t%p\n",&sa);
printf("&sb =\t%p\n",&sb);
printf("&sc =\t%p\n",&sc);
printf("Func1 =\t%p\n",&Func1);
printf("Func2 =\t%p\n",&Func2);
printf("Func3 =\t%p\n",&Func3);
char* s1 = "abcd";
char* s2 = "1234";
char* s3 = "!@#$";
printf("s1 =\t%p\n",s1);
printf("s2 =\t%p\n",s2);
printf("s3 =\t%p\n",s3);
}
&a = 0x7ffdf94c1d1c //局部变量存放在栈区,相邻变量地址递减
&b = 0x7ffdf94c1d18
&c = 0x7ffdf94c1d14
p1 = 0xe95670 //申请内存存放在堆区
p2 = 0xe95690
p3 = 0xe956b0
&ga = 0x601030 //全局变量存放在数据区
&gb = 0x601034
&gc = 0x601038
&sa = 0x60103c //静态局部变量存放在数据区
&sb = 0x601040
&sc = 0x601044
Func1 = 0x4005d6 //存放在代码区
Func2 = 0x400632
Func3 = 0x400639
s1 = 0x400978 //字符串地址递增
s2 = 0x40097d
s3 = 0x400982
Func:&a = 0x1
Func:&b = 0x2
Func:&c = 0x3
上面结果每次执行结果会有所变化,不同计算机结果也会不同。但是会有如下规律:
- 局部变量、静态变量、动态分配内存、字符串常量与函数分别放在一起,即使在不同的函数中。
- 变量的存放地址大小有如下特点:
字符串常量与代码 < 静态变量 < 动态分配内存 < 局部变量
- 静态变量、动态分配内存、字符串常量与函数的相邻变量地址是递增的。局部变量相邻变量地址是递减的。
- 字符串常量与函数是在一起的。