随着深入的学习C语言,这门语言是与内存息息相关的,那么为了更好地认识这门语言,我们就要从内存深入剖析,此篇博客来浅谈一下C语言的内存布局。
- 运行中的C语言内存布局
上图中显示了,C语言的内存布局,可以看到的区域有,文本段,初始化数据端、未初始化数据段,还有堆与栈。 - 分析各段作用
(1) 文本段(.text)
文本段,也叫作代码段;是目标文件(二进制文件)或内存中包含可执行程序的一部分。作为存储区,文本段放在堆和栈区下面,以防止堆栈溢出覆盖它。文本段通常是only read(仅仅可读),就是为防止程序意外修改其中的指令。
(2)初始化数据段(.data)
初始化数据段,通常被叫做,数据段;数据段是程序的虚拟地址空间的一部分,其中包含由人们初始化的全局变量和静态变量。这段的数据不是仅仅可读的,因为变量的值可以在运行时修改它们的值。
对该段又可以细分为:初始化的只读区域和初始化的读写区域
举例说明:
C语言中定义全局变量char arr[] = “hello world~~”; 再定义局部变量char arr1[] = “hello world~~”; 二者都将存储在初始化读写区域中;但是如果像这样定义全局变量,const char *string = “hello world~~”; 那么将存储在初始化只读区域中,而字符串指针变量将存储在初始化读写区域中。
(3)未初始化数据段(.dss)
未初始化数据段,通常叫做“bss”段;在该段中将放置未初始化的静态变量和未初始化的指针,并且初始化为0和空指针。未初始化的数据从数据段的末尾开始,包含初始化为0或者没有初始化的全局变量和静态变量;
举例说明:(都未初始化)
1 #include <stdio.h>
2 static int j;
3 int i;
4 int main(){
5 static int k;
6 printf("%d %d %d\n", i, j, k);
7 return 0;
8 }
输出:
我们将使用一个开发工具—size
;来观察内存分布情况
。
1 #include <stdio.h>
2
3 int main(){
4
5 return 0;
6 }
接着,在程序中添加一个全局变量,现在检查.bss的大小
1 #include <stdio.h>
2
3 int global; //未初始化全局变量存储在.bss中
4
5 int main(){
6
7 return 0;
8 }
对比前后变化,发现.bss发生了变化。
我们再来添加一个未初始化静态变量,它也存储在.bss中
1 #include <stdio.h>
2
3 int global; //未初始化全局变量存储在.bss中
4
5 int main(){
6
7 static int i; //未初始化的静态变量存储在.bss值被初始化为0
8
9 static int j; //未初始化的静态变量存储在.bss值被初始化为0
10 return 0;
11 }
现在我们初始化,静态变量的值,将存储字数据段中
1 #include <stdio.h>
2
3 int global; //未初始化全局变量存储在.bss中
4
5 int main(void){
6
7 static int i = 100; //初始化的静态变量存储在数据段
8
9 static int j = 100; //初始化的静态变量存储在数据段
10 return 0;
11 }
可以看到,.bss变小,.data变大;
我们再来给全局变量初始化,观察情况:
1 #include <stdio.h>
2
3 int global = 100; //初始化全局变量存储在数据段中
4
5 int main(void){
6
7 static int i = 100; //初始化的静态变量存储在数据段
8
9 static int j = 100; //初始化的静态变量存储在数据段
10 return 0;
11 }
相信通过以上的分析,可以直观的看到数据是如何在.bss和data中存储的。
(4)堆栈
普通的局部变量在栈空间上分配。
堆栈区一般与堆区(heap)相邻,并且向相反的方向(低地址)增长;当堆栈指针与堆指针相遇时,可用的内存表示耗尽。堆栈区域包含程序堆栈,LIFO结构,通常位于内存比较高的地方。
堆栈区发生C语言中的函数调用,当函数调用返回地址和有关调用的信息都保存在堆栈区;接着新调用的函数在堆栈上方为其自动和临时变量分配空间,这就是C语言递归函数的工作方式。每次递归函数调用自身,都会使用新的堆栈帧,因此一组变量不会干扰来自函数的另一个实例中的变量。
(5)堆
堆区是经常发生动态内存分配的区域。堆区从.bss的末尾开始,并从末尾增长到更大的地址。(堆区由malloc,realloc和free管理,可以使用brk和sbrk系统调用来调整其大小也可以使用mmap实现。)
由于本人也是初次了解学习,所有有任何错误请大家指正。
参考书籍:《unix系统高级编程》