C语言 Memory Layout

虽然自己的工作是和C语言天天打交道,但深究其根本,却发现有很多基础的问题,并不曾了解。比如组成“Hello world !”的是如何被加载到计算机并显示到你的屏幕上的?gcc编译出的可执行文件由哪些部分组成?最近正巧在研究这方面的内容,刚好和大家分享一下。

我们知道C语言和Python不同,Python是一门解释性语言,而C是一门编译语言。解释性语言的程序不需要编译,省了道工序,在程序运行中进行实时翻译,而C需要进行编译后才能在计算机上运行。

编译型与解释型,两者各有利弊。前者由于程序执行速度快,同等条件下对系统要求较低,因此像开发操作系统、大型应用程序、数据库系统等时都采用它,而一些网页脚本、服务器脚本及辅助开发接口这样的对速度要求不高、对不同系统平台间的兼容性有一定要求的程序则通常使用解释性语言。

典型 C Memory Layout

C语言 Memory Layout:原文配图 1

一个典型的C语言程序加载到内存中分为以下几个部分:

命令行参数

栈

堆

未初始化的数据段有初始值的数据段

代码段

对于静态链接生成的可执行文件来说,基本就是由这六个部分组成;如果是动态链接,可能还有动态链接库加载区。关于动态链接,是一个很复杂的部分,哪天抽出时间来单独写一篇,本文我们先来谈谈静态链接中的内存分布。

由静态链接生成出来的可执行性文件,其堆和栈会在程序运行中不断变化,而其他部分并不会随着程序的运行而发生改成。

动态内存分布:栈,堆

静态内存分布:数据段,代码段

可以认为,静态内存分布在编译链接完成后就确定了,而动态内存分布会随着程序的不断运行而发生改变。

命令行

在我们运行程序时,操作系统会将以下两个部分的内容送入内存:

环境变量

命令行参数

相关参数将会被压入栈中,在栈中产生字符指针数组,并分别指向这些字符串,作为main的形参输入。我们知道C语言中的main函数有两个形参的,argc表示送入命令行字符串的个数,argv数组里面装的是命令行字符指针。

  1 int main(int argc, char* argv[])

栈

栈是函数的基础,可以说没有栈就没有函数的出现。栈的空间是向下增长的,遵循“先进后出”的原则。它只有两个操作,push:将数据压入栈;pop:将数据压出栈内并返回。

C语言 Memory Layout:原文配图 2

有两个关键的寄存器记录着栈中地址的变化,分别是扩展基址指针寄存器ebp( Extended base pointer ) 和 扩 展 堆 栈 指 针 寄 存 器 esp ( Extended stack pointer)。

ebp:指向栈帧的底部,高地址

esp:指向栈帧的顶部,低地址

没想到吧,就是通过这两个小小的寄存器,就实现了C语言函数的跳转工作。栈内存储着函数的输入参数,局部变量,临时信息/数据,返回地址等。当使用递归函数时,由于函数层级较深,可能会出现栈溢出的情况哦!

堆

没有堆,就没有动态内存。堆是一块巨大的内存空间,常常占据着整个虚拟空间的绝大部分。在这片空间里,程序可以请求一块连续内存,并自由地使用,这块内存在程序主动放弃之前都会一直保持有效。

进程通过调用如malloc来向堆申请动态内存。堆内所有内容都是匿名的,只能通过指针进行访问。与栈不同的是,堆的空间是向上增长的。由于堆内存的申请与回收是动态变化的,对于堆的管理就显得尤为重要。

  1 int main()
  2 {
  3 char *p = (char*)malloc(1000);
  4 free (p);
  5 }

bss & data

bss segment :初始化为零的全局和静态变量。

data segment :初始化为非零的全局和静态变量。

对于Linux / Unix环境,可执行文件中只有初始化为非零值的变量部分才会占用磁盘空间。牛刀小试一把,在Linux环境下,以下三个变量将会被分配在什么区域?

  1 int A ;
  2 int B = 0;
  3 int C = 10;

正确答案:因为编译器会把为未初始化的和初始化为0的全局变量定义在bss区域,只有非0的初始化才会定义到data区。bss:A,B data:C

text / code

text/code segment,顾名思义是用来存放可执行指令的,C代码编译后生成的二进制指令就会存放在这个区域。此处需要注意是,系统可能将此区域视为只读(only read),任何对该区域进行写入操作的尝试,操作系统都会认为是非法操作并阻止。

关于编译和链接相关的内容比较复杂,如果大家感兴趣的话,还可以看看以下文章:

第28期:编译与链接

{荐书} 第2期《程序员的自我修养》

完

推荐阅读

C语言 Memory Layout:原文配图 3

↙戳链接一秒找到我有观点分享,请留言↘

编程 · 目录

上一篇 下一篇

「C语言」指针数组 数组指针 指针函数 函数 早在盘古开天之前 (Main的故事)

指针

阅读原文

← 返回文章列表