C/C++内存布局策略详解

详解C/C++的内存布局,以及C++对象内存布局,以及C和C++内存布局之间有什么区别。


C程序内存布局

Linux C程序内存布局

主要分为以下几部分组成:

  • 代码段
  • 初始化数据段(数据段)
  • 未初始化数据段(BSS段)

  • 以下分别来介绍各部分的作用。

代码段

代码段是由程序中的机器代码组成。在C语言中,程序经过编译后,形成机器代码。在执行的过程中,CPU的程序计数器指向代码段中的一条指令,依次执行。


初始化数据段

初始化数据段中数据又可分为只读数据段和读写数据段,其中只读数据段是程序使用的一些不会被更改的数据,比如字面值常量,由于这些变量不需要修改,因此只需放置在只读寄存器中即可。读写数据段放置的是在程序中声明的,具有初始值的变量,比如已初始化的全局变量和静态变量。


未初始化数据段

未初始数据段也称之为BSS段,即Block Started by Symbol,在BSS段中存放的数据都是未被初始化的全局变量和静态变量,并默认会把这部分数据初始化为0。


堆是动态内存分配区,向高地址增长,堆中分配内存和释放内存需要调用malloc/new或free/delete。


栈和堆一般相邻,但沿着相反方向增长。当栈指针和堆指针相等就说明堆栈内存耗尽。栈中保存的是局部变量和每次函数调用时的信息。每次函数调用返回地址,一些调用者环境信息(比如寄存器)都被存放在栈中。然后新调用的函数就会在栈中为他们的自动或者临时变量分配内存空间,这就是C中递归函数调用的过程。每次递归函数调用自己,新的堆栈就会被创建,这样新的变量集合就不会被其他函数实例的变量集合影响了。


实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const char ro[] = {"only read data area"}; // 初始化只读存储区
static char rw = {"read write data area"}; // 初始化读写数据段
char bss[100]; // BSS段
const char *pstrconst = "constant data"; // 初始化只读存储区
int main(int argc, char argv[]) { // 命令行参数防止在栈地址上的命令行参数空间中
int a; // 栈上
char a[100]; // 栈
char *c = "abcdef"; // c在栈上,"abcdef"在只读存储区,占7个字节
char *p; // 栈
static int rw2 = 0; // 初始化读写数据段
static char bss2[100]; // BSS段
p = (char *)malloc(10*sizeof(char)); // 在堆上分配10个字节
free(p);
return 0;
}

C++程序内存布局

有关C++的内存布局,网上基本上都是按照C程序的内存布局来讲解,这其实是错误的,C程序和C++程序的内存布局是不一样的,在C++中内存可以分为以下5个部分:

  • 代码段:这部分与C程序是大致相同的;
  • 常量存储区:这部分存储的内容与C语言中初始化数据段中的只读数据段是一样的,用来存储C++常量;
  • 全局/静态存储区:在C++中,不再区分数据段和BSS段,未初始化和初始化的全局/静态变量都会存储在这里,并且初始化为0;
  • 堆:堆又分为new分配的存储区和malloc分配的内存块,new分配的存储区会在程序结束之后,系统会帮助我们清理;
  • 栈:栈和C程序的栈相同。
    C和C++的内存布局大致上是相同的,主要区别在于C++引进了对象,C++对象中的成员函数存储在代码段中,数据成员才会存储在栈中,同样静态变量会存储在在全局/静态存储区,并且必须初始化。当然C++对象内存布局会涉及到继承,多态而出现多种变化,以及内存对齐的影响,对于这部分的内容参考:
    内存对齐策略
    C++之继承
    C++之多态

参考资料
C语言的内存布局
正确的C++/C内存布局及两者的区别