C/C++内存模型
C语言编程中的内存基本模型
C的内存基本上分为4部分:静态存储区、堆区、栈区以及常量区。他们的功能不同,对它们使用方式也就不同。
-
栈 ——由编译器自动分配释放。
-
堆 ——一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。
-
全局区(静态区)——全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域(data段),未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(bss段)( C++中已经不再这样划分),程序结束释放。
-
另外还有一个专门放常量的地方,程序结束释放:
- 函数体中定义的变量通常是在栈上;
- 用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上;
- 在所有函数体外定义的是全局量;
- 加了static修饰符后不管在哪里都存放在全局区(静态区);
- 在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用;
- 在函数体内定义的static表示只在该函数体内有效;
- 另外,函数中的"adgfdf"这样的字符串存放在常量区。
一个正在运行着的C编译程序占用的内存分为5个部分:代码区、初始化数据区、未初始化数据区、堆区 和栈区。
C++编程中的内存基本模型
在C++中内存分成5个区,分别是堆、栈、全局/静态存储区、常量存储区和代码区;
-
栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区,里面的变量通常是局部变量、函数参数等。
-
堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收
-
全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
-
常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改)。
-
代码区 ( .text段),存放代码(如函数),不允许修改(类似常量存储区),但可以执行(不同于常量存储区)。
- .text
代码区(code section)。由编译器链接器生成的可执行指令,程序执行时由加载器(loader)从可执行文件拷贝到内存中。为了安全考虑,防止别的区域更改代码区数据(即可执行指令),代码区具有只读属性。另一个方面,代码区通常具有可共享性(sharable),即在内存中只有一份代码区,如编译器,假如同时有多个编译任务在执行,这些编译任务会共享编译器的代码区,但同时各个编译任务又有自己独立的区域。 - rodata
只读数据区(read-only section)。包含:只读全局变量,只读字符串变量,只读静态(static)变量。程序执行时由加载器loader)从可执行文件拷贝到内存中。 - data
可写数据区(RW section)。包括:可写全局变量,可写静态(static)变量。程序执行时由加载器(loader)从可执行文件拷贝到内存中。 - bss
未初始化数据区(un-initialized section)。包括:未初始化或初始化为零的全局变量,未初始化或初始化为零的静态(static)变量。为了减小可执行文件的大小,在可执行文件中bss区只是一个占位符。在程序执行时,加载器(loader)根据bss区的大小,在内存中开辟相应空间,同时将这些内存空间全部初始化为零。
.text, .rodata, .data, .bss四个区域,统称为编译时内存(compiler-time memory),顾名思义,这些区域的大小在编译时就可以决定。 - heap
堆区。对于C语言而言,heap指程序运行时(run-time)由malloc, calloc, realloc等函数分配的内存。 - stack
栈区。每一次函数调用,都会发生一次压栈操作,被压栈数据称为一个栈帧(stack frame),有多少次函数调用(包括main()函数),栈区就有多少个栈帧。相应的,每一次函数调用返回,都会相应的发生一次出栈操作,栈帧就会减少一个。
函数调用时,根据压栈的顺序,依次需要压栈的数据包括:调用函数(caller funtion)的上下文环境(context environment),如寄存器;函数返回地址;被调用函数(called funtion)的参数列表;被调用函数的非静态(static)局部变量。
当栈区溢出(stack overflow/underflow)时,栈区数据被污染,程序执行错误,甚至“跑飞"(函数返回地址被修改)。