C语言笔记之02-C语言程序内存布局
我们知道C语言的编译过程分为:预处理-编译-汇编-链接-生成可执行文件,而这个可执行文件由代码段(text)、数据段(data)、BSS段(bss)三部分组成。在执行可执行文件的时候,需要将这个可执行文件装配到内存中才能运行,这个中间涉及到一个C程序的内存的布局,主要是变量、常量和函数的存放位置,因此需要搞懂它的内存布局,才能很好理解程序的内存结构和优化。
内存映射
下图是C程序的内存模型
Linux下32位环境的用户空间内存分布代码段(text)
代码段存放的是可执行文件的可执行指令,从图中可以看出,代码段是放在内存最低地址处,这段区域是只读区域,也是可共享的。装配时直接将该段代码直接拷贝到内存的.text区。
常量区(data)
常量段也是整个数据段的一部分,主要存储常量和字符串常量,该段也是只读区域。
全局数据区(data)
全局数据区也是数据段的一部分,主要存储全局变量、static变量,该段有两部分组成:可读写数据区和只读数据区。
未初始化数据区(bss)
bss存放未初始化的全局变量和static变量
堆段(heap)
heap是写程序中经常遇到的一个区域,这块区域是动态内存分配区,堆地址从内存低地址往高地址增长,完全由程序猿自己分配和释放,这个地方一般最容易出现内存没有释放导致程序crash。在C语言中经常使用的函数就是malloc/calloc/realloc/free.
栈段(stack)
stack是函数执行和调用中打交道最多的地方,这块区域专门存放函数的参数、局部变量,栈的地址是由高到底进行分配,与堆正好相反。一般程序中的栈空间是有限的,其只是程序的一个临时存储区,所以在函数内部申请大的内存必须要使用堆来申请,切记不能使用数组来申请大的空间。
实例
一般面试题经常会考到的地方就是关于字符串常量的指针是否相等,如这个例子中的str1、str3,str7,str8是指向同一个内存地址。
#include <stdio.h>
#include <stdlib.h>
char *str1 = "helloworld"; //字符串helloworld放在已初始化数据区(常量区),str1放在全局数据区
char str2[] = "helloworld";//字符串helloworld和str2放在全局数据区
static char *str3 = "helloworld";//字符串helloworld放在已初始化区(常量区),str3放在全局数据区
static char str4[] = "helloworld";//字符串helloworld和str4放在全局数据区
char *str5;//str5放在全局未初始化数据区
static char *str6;//str6放在全局未初始化数据区
int a1;//全局未初始化数据区
static int a2;//全局未初始化数据区
const char *str11;//全局未初始化数据区
int main(int argc, char **argv){
static char *str7 = "helloworld";//helloworld是字符串常量放在常量区,str7放在全局数据区
char *str8 = "helloworld";//helloworld是字符串常量,str8放在栈区
char str9[] = "helloworld";//helloworld不是字符串常量,都放在栈区
char *str10 = (char *)malloc(sizeof("helloworld"));//str10放在堆区
const char *str12;//栈区
char *str13;
printf("str1 %p, str2 %p, str3 %p, str4 %p\n", str1, str2, str3, str4);
printf("str5 %p, str6 %p\n", str5, str6);
printf("a1 %p, a2 %p\n", &a1, &a2);
printf("str7 %p, str8 %p, str9 %p, str10 %p\n", str7, str8, str9, str10);
printf("str11 %p, str12 %p, str13 %p\n", str11, str12, str13);
if (str10){
free(str10);
str10 = NULL;
}
return 0;
}
这个例子中最容易混淆的地方就是char str9[] = "helloworld",数组形式定义的并不是字符串常量。
网友评论