目录
1.地址空间分布
在C语言中,每个C程序加载进内存时,操作系统都会给它分配虚拟地址空间,一般大小为4G,地址空间分布情况如下:
从上到下分别为代码区,字符常量区,已初始化全局变量区,未初始化全局变量区,堆区和栈区 ,其中堆栈相对而生。
注:C程序地址空间不是内存,本质上是虚拟地址。
2.各区域内容
下面是各个区域的名称以及所存放的内容(地址由低到高):
地址空间区域 | 内容 |
栈区 | 局部变量,函数所形成的栈帧 |
堆区 | malloc,realloc等所申请的动态空间 |
未初始化全局变量区 | 未初始化的全局变量,static修饰的变量 |
已初始化全局变量区 | 已初始化的全局变量,static修饰的变量 |
字符常量区 | 字符串常量 |
代码区 | 代码(包括函数) |
3.验证地址空间排布
我们可以根据每个区域存放的内容来定义多种数据类型,通过它们的地址来验证各区域的相对位置。(注:由于windows具有栈随机化保护的特性,得出的结果可能有所不同,因此我们将在Linux系统下进行验证。)
#include<stdio.h>
#include<stdlib.h>
int g_val2; //未初始化全局变量
int g_val1 = 10; //已初始化全局变量
int main()
{
printf("code addr: %p\n", main); //函数地址,代表代码区,函数是代码的一部分
const char* str = "hello world"; //str存放字符串常量的首地址,指向字符常量区
printf("read only: %p\n", str); //字符常量区
printf("init g_val: %p\n", &g_val1); //已初始化全局变量区
printf("uninit g_val: %p\n", &g_val2); //未初始化全局变量区
char* p = (char*)malloc(10 * sizeof(char)); //动态内存开辟,p指向堆区
char* p1 = (char*)malloc(10 * sizeof(char));
char* p2 = (char*)malloc(10 * sizeof(char));
printf("head addr: %p\n", p); //堆区
printf("head addr: %p\n", p1);
printf("head addr: %p\n", p2);
printf("stack addr: %p\n", &str); //栈区
printf("stack addr: %p\n", &p);
printf("stack addr: %p\n", &p1);
printf("stack addr: %p\n", &p2);
return 0;
}
在Linux系统运行结果如下:
我们不难发现,各区域所在地址从上到下是呈递增的趋势的。所以地址从低到高分别是代码区,字符常量区,已初始化全局变量区,未初始化全局变量区,堆区和栈区,符合一开始的地址空间分布图。
对于堆区,我们定义了先后开辟的3个堆空间,并将地址保存在p,p1,p2中。通过p,p1,p2存放的地址呈递增趋势我们可以得出堆区的增长方向是从低地址向高地址增长的。
对于栈区,由于变量str,p,p1,p2是在栈上先后开辟的,我们通过&取得它们的地址。根据打印的结果我们发现地址呈递减趋势可以得出栈区的增长方向是从高地址向低地址增长的。因此,堆栈相对而生。
值得注意的是,堆区和栈区的地址位数差了6位,说明两个区域之间有很大的镂空。
以上,就是本期的全部内容。
制作不易,能否点个赞再走呢qwq