C内存模型
前言
本文以Linux为例,讲解Linux下C程序如何在内存中体现的。
ELF文件
在认识程序如何在内存中体现的,我们首先要了解一下Linux下的可执行文件ELF(Executable and Linkable Format)。
通常在使用linux时,在/bin或者/usr/bin下面可以看到许多可执行文件
我们用file命令查看其中一个可执行文件,比如ls,就可以看到下面这段描述
这就是elf文件,我们平常执行的命令其实就是一个个elf可执行文件。
粗略结构如下:
elf文件结构
- elf header 描述整个文件的组织
- program header table 描述文件中各种段,告诉内存以只读、可读写等的方式映射程序中的段
- section header table 描述文件中的各个节区,比如.text、.data,一般给程序编译连接阶段提供信息
- 剩下各个节区
- .text程序二进制代码,.rodata只读数据,这两个和program header table、elf header一并属于只读代码段
- .data已初始化全局/静态变量、.bss未初始化全局变量/静态变量,一并属于读写数据段
- 其余节区和section header table一并属于另一个段,不用加载在内存中
内存分区
当elf可执行文件运行时,操作系统根据program header table中定义的段,将对应的段根据定义信息加载到内存中。当然,除了将程序本身加载在内存中,程序,程序运行时也会动态创建栈区、堆区。具体的内存布局为下面几个部分:
.text 代码段,存放程序的可执行代码,不可修改
.rodata(Read Only Data) 常量区,存放全局常量
.data 数据段,存放已初始化的全局变量和静态变量
.bss(Block Started By Symbol) 未初始化数据段,存放未初始化的全局变量或者初始化为0的全局变量和静态变量
heap 堆区,在内存中总体从低地址向高地址增长,不一定是连续的,由程序员动态申请,在程序运行时才开辟内存空间
stack 栈区,在内存中由高地址向低地址增长,保存程序栈帧,栈帧里包含函数返回地址、函数参数、函数内部定义的变量
如下图:
具体代码解释
以下面这段代码为例:- #include<stdio.h>
- #include<stdlib.h>
- char c1; //字符变量c1未被初始化,全局变量编译器默认初始化为空格,放入bss段中
- char c2='a'; //字符变量c2被初始化为'a',放入data数据段中
- const int i1=1; //整形变量被const修饰,为常量,放入rodata只读数据段中
- int main(int argc, char const *argv[]){ //main函数及其调用的子函数的内部变量、参数、返回地址,在程序运行时动态放入栈区中
- int *ip=(int*)malloc(5*sizeof(int)); //这里是在运行时动态申请内存,申请的内存放在堆区中
- ...
- }
- //以上整个代码编译后的二进制数据,放入text代码区中
复制代码 更复杂的情况如下,函数中一个字符串常量赋值给一个字符串数组,类似于下面代码:- int fun(){
- char s[]="Hero";
- ...
- return 0;
- }
复制代码 字符串“Hero”是常量,放在在.rodata只读数据段里的,但是一个函数在被调用时会在栈区形成一个栈帧,它包含了函数内部定义的变量,所以字符数组s会在fun函数被调用时从.rodata里复制“Hero”到栈区中字符数组s的地址,“Hero”大小为5个字节(包含‘\0’),栈区中的字符数组s也是5个字节
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |