找回密码
 立即注册
首页 业界区 安全 C内存模型

C内存模型

第璋胁 6 天前
C内存模型

前言

本文以Linux为例,讲解Linux下C程序如何在内存中体现的。
ELF文件

在认识程序如何在内存中体现的,我们首先要了解一下Linux下的可执行文件ELF(Executable and Linkable Format)。
通常在使用linux时,在/bin或者/usr/bin下面可以看到许多可执行文件
我们用file命令查看其中一个可执行文件,比如ls,就可以看到下面这段描述
1.png

这就是elf文件,我们平常执行的命令其实就是一个个elf可执行文件。
粗略结构如下:
2.png

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 栈区,在内存中由高地址向低地址增长,保存程序栈帧,栈帧里包含函数返回地址、函数参数、函数内部定义的变量
如下图:
3.png

具体代码解释

以下面这段代码为例:
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. char c1;        //字符变量c1未被初始化,全局变量编译器默认初始化为空格,放入bss段中
  4. char c2='a';        //字符变量c2被初始化为'a',放入data数据段中
  5. const int i1=1;        //整形变量被const修饰,为常量,放入rodata只读数据段中
  6. int main(int argc, char const *argv[]){        //main函数及其调用的子函数的内部变量、参数、返回地址,在程序运行时动态放入栈区中
  7.         int *ip=(int*)malloc(5*sizeof(int));        //这里是在运行时动态申请内存,申请的内存放在堆区中
  8.         ...
  9. }
  10. //以上整个代码编译后的二进制数据,放入text代码区中
复制代码
更复杂的情况如下,函数中一个字符串常量赋值给一个字符串数组,类似于下面代码:
  1. int fun(){
  2.         char s[]="Hero";
  3.         ...
  4.         return 0;
  5. }
复制代码
字符串“Hero”是常量,放在在.rodata只读数据段里的,但是一个函数在被调用时会在栈区形成一个栈帧,它包含了函数内部定义的变量,所以字符数组s会在fun函数被调用时从.rodata里复制“Hero”到栈区中字符数组s的地址,“Hero”大小为5个字节(包含‘\0’),栈区中的字符数组s也是5个字节

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册