一个典型的C程序存储分区包含如下几类:架构
1. Text段函数
Text段一般也称为代码段,由可执行指令构成,是程序在目标文件或内存中的一部分,Text段一般放在栈或堆的下面,以防止堆栈溢出篡改其数据。布局
一般状况下,Text段是可共享的,对于须要频繁调用的程序,其在内存中只须要一份拷贝便可,如文本编辑器、C编译器、Shell等,所以text段一般设为只读以防止程序的突发性的修改。翻译
2. 已初始化数据段debug
已初始化数据段,一般简单称做数据段,数据段占据程序虚拟地址空间的一部分,内部包括全局变量、静态变量(程序负责初始化这些变量)。需注意的是,数据段不是只读的,在运行时变量值是能够变更的。指针
数据段还能够更细的分为初始化只读区以及初始化可读写区。cdn
举例:全局字符串 char s[] = “hello world”,全局变量 int debug=1,静态变量 static int i = 10 存储在初始化可读写区;另外一种状况下,const char* string = “hello world”,字符串“hello world”存储在初始化只读区,string指针则存在初始化可读写区。blog
3. 未初始化数据段递归
未初始化数据段,一般称做“bss”段,名字来源于古老的汇编操做符命名 “block started by symbol”,段内的数据在程序开始执行以前被内核初始化为0值,一般开始于已初始化数据段的末尾处。段内包含初始化为0的全局变量/静态变量以及源码中未显示进行初始化的变量。
举例:变量 static int i; 全局变量 int j; 包含在BBS段中。
4. 栈
栈与堆是相互毗邻的,而且生长方向相反;当栈指针触及到堆指针位置,意味着栈空间已经被耗尽(现在地址空间愈来愈大,及虚拟内存技术发展,栈与堆可能放置在内存的任何地方,但生长方向依然仍是相向的)。
栈区域包含一个LIFO结构的程序栈,其一般放置在内存的高地址处,在x86架构中,栈朝地址0方向生长,在其它架构也可能朝着相反的方向生长。栈指针寄存器跟踪栈顶位置,每当有数值被压入栈中,栈顶指针会被调整,在一个函数的调用过程当中,压入的一系列数值被称做“栈帧”,栈帧至少包含一个返回地址。
栈存储着自动变量以及每次函数调用时保存的信息,每当函数被调用时,返回地址以及调用者的上下文环境例如一些机器寄存器都存储在栈中,新的被调用函数此时会在栈上从新分配自动/临时变量,这就是递归函数的工做原理。每当函数递归调用本身时,都会使用新的栈帧,所以当前函数实体内的栈帧内的变量不会影响另一个函数实体内的变量。
5. 堆
堆一般用做动态内存分配,堆空间起始于BSS段的末尾,并向高地址处生长,堆空间一般由malloc, realloc 及 free管理,这些接口可能再使用brk/sbrk系统调用来调整大小,在一个进程中,堆空间被进程内全部的共享库及动态加载模块所共享。
示例-查看可执行文件的存储分配
注:size命令以字节为单位统计可执行程序的text, data, 及bss段(更多详情参考man page of size(1))
1. 检查下述C程序
编译后查看text/data/bss分布状况
2. 增长一个全局变量
编译后观察变化,bss区域增长了4字节
3. 再添加一个静态变量
编译后再看变化,bss区域增长了4字节
4. 初始化3中的静态变量看看
此时变量存储到了data段,data增长了4字节,bss减小了4字节
5.继续初始化2中的全局变量看看
此时静态变量也存储到了data段,data增长了4字节,bss减小了4字节
6. 添加一个全局const变量看看
此时“haha”字符串存储到了text段,并增长了5个字符,多出来的一个字符是\0,ptr指针存储到了data段,增长了4个字节。
注:在步骤4/5中,初始化的值若为0,编译器仍是会将变量放入BSS区。感兴趣的同窗能够动手编译看看。
文章翻译/修改自 https://www.geeksforgeeks.org/memory-layout-of-c-program/