一、程序运行内存分布图
我们知道一个由我们编写好的程序,运行时,我们的程序中写的代码,定义的变量,写的函数、for 循环等等,这些运行时都分布在内存中的哪里吗?
一下是一个程序运行时 内存的各个区域的分布
详细的每个区域存放的内容如下
二、栈
没错,是不是想到了 数据结构的栈:
栈 遵循后进先出,先进后出的 存储顺序,那么这个栈和我们的程序运行又有什么大的联系呢?
看一段代码示例:
#include <stdio.h>
void Fun4()
{
printf("4\n");
}
void Fun3()
{
printf("3\n");
Fun4();
}
void Fun2()
{
printf("2\n");
Fun3();
}
void Fun1()
{
printf("1\n");
Fun2();
}
int main()
{
Fun1();
return 0;
}
是的 是一段非常无聊且易懂的代码,函数调用关系图为:
、
调试代码并 打开VS2017 的堆栈窗口
该图表示函数的调用过程 自底向上调用,函数执行完之后,该函数生命周期结束,函数退出,就像栈的 pop 操作,直到所有的函数调用完之后,栈底只有一个 main 函数 , main 函数执行完之后 最终整个程序也结束掉。
栈的空间一般是由系统分配的,一般存储着局部变量、函数参数、函数返回值等,一般大小为 1M 、2M 等 ;
在内存中的位置为:
且是向下分配空间
示例局部变量定义:
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
int c = 0;
printf("%p\n", &a);
printf("%p\n", &b);
printf("%p\n", &c);
return 0;
}
可以看到 局部变量 a b c 的地址依次变低,也证实了栈的空间地址分配时 从高到低
三、堆
注意 这个堆 可不是 数据结构里面的堆, 这里的堆 是专门开辟出来 给我们程序员 动态申请内存使用的,例如 我们 malloc 、new 出来的空间 这部分都在堆上
与栈相反的是,我们动态申请的空间是 由低地址到高地址的
代码示例:
#include <stdio.h>
int main()
{
int *p = (int*)malloc(8);
int *p1 = (int*)malloc(8);
printf("%p\n", p);
printf("%p\n", p1);
//同时 使用完申请的空间之后要记得释放,否则会造成内存泄漏,
//且释放之后,不能再次释放 否则会造成非法操作、越界访问,导致程序崩溃
free(p);
p = NULL;
free(p1);
p1 = NULL;
return 0;
}
打印结果:
3.2 堆与栈的区别
空间管理方式不同:
1.栈上的空间由系统分配并管理、不需要我们关心申请与释放
2.堆上的空间是我们自己申请并负责释放、如果忘记释放会造成内存泄漏、多次释放也会导致越界访问、非法操作、甚至导致程序崩溃问题等,这是需要我们程序员自己注意的地方
内存分配方向不同
1.栈上的空间是从高地址向低地址分配
2.堆上的空间是从低地址向高地址分配
分配效率不同
1.栈是由系统分配管理,更加贴合系统底层、效率更高
2.堆的申请和释放是由库函数支持,由我们自己手动调用库函数进行申请内存和释放内存,且容易产生内存碎片的问题,效率低于栈
存放内容不同
1.栈主要存放局部变量、函数返回值、函数参数、函数地址等、程序运行过程中的函数调用就是栈来完成的、函数调用逐个入栈、调用完成之后逐个弹出
2.堆主要为我们动态申请空间服务,为我们开辟空间
四、全局区/静态存储区
这里主要存放 全局变量和 静态变量、生命周期覆盖整个程序、直到程序结束,生命周期结束、不过这里还细分了是否初始化的全局变量和静态变量,但是大体是在一个区域内存放
例如:
#include <stdio.h>
int n = 0;//全局区
int arry[10];//全局区
int main()
{
static int i = 0;//静态存储区
return 0;
}
五、常量区
例如如下:
string str1 = "helloworld";
int n = 6;
"helloworld 和 6 都属于常量、不允许被修改
六、代码区
存放代码、不允许被修改,也就是我们所写的函数,是存放在这里的
代码区中的东西是随整个程序一起的,启动时 生、结束时 亡
有时候放在代码段的不只是代码,还有const类型的常量,还有字符串常量。(const类型的常量、字符串常量有时候放在常量区有时候放在代码段,取决于平台)