每日一问11:C++程序的内存格局
每日一问11:C++程序的内存格局
网上对于C++程序的内存格局有两种分法,这里两种分法都学习一下。
四分法
四分法是更为常见的分法,其内存格局如下:
其中,
- 栈区,栈又称堆栈, 内存由编译器在需要时自动分配和释放。通常用来存储局部变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,栈用来传递参数和返回值。栈运算分配内置于处理器的指令集中,效率很高,但是分配的内存容量有限。(为运行函数而分配的局部变量、函数参数、返回地址等存放在栈区)。
- 堆区,堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc/free等函数分配内存时,新分配的内存就被动态添加到堆上 (堆被扩张)/释放的内存从堆中被剔除(堆被缩减)。
- 全局数据区,存放全局变量、静态数据和常量;(在程序编译时期分配好的,在程序的整个运行期间都存在)。
- 代码区,所有类成员函数和非成员函数代码存放在代码区。该区是只读的,任何尝试对该区的写操作会导致段违法出错。代码区是被多个运行该可执行文件的进程所共享的。
五分法
这种分法来源于《C++内存管理技术内幕》一书,将C++中的内存分为了5个区,分别是:堆,栈,自由存储区,全局/静态存储区,常量存储区。 这种分法,没有提到代码区,将全局数据区细分为全局/静态存续区和常量存续区,将堆区分为堆和自由存储区。
-
栈区,同上。
-
堆区,内存使用new进行分配,使用delete或delete[]释放。如果未能对内存进行正确的释放,会造成内存泄漏。但在程序结束时,会由操作系统自动回收。
-
自由存储区:使用malloc进行分配,使用free进行回收。和堆类似。
-
全局/静态存储区:全局变量和静态变量被分配到同一块内存中,C语言中区分初始化和未初始化的,C++中不再区分了。
-
常量存储区:存储常量,不允许被修改。
更为完善的结构
一个进程在内存中的布局:
其中,BBS段含了程序中未初始化全局变量,在内存中bss段全部置零。
程序样例
//main.cpp
static int s = 1; //全局静态变量 (全局/静态存储区)
int a = 0; //全局初始化区(全局/静态存储区)
char *p1; //全局未初始化区 (BSS)
int main()
{
int b; //栈
char s[] = "abc"; //栈
char *p2; //栈
char *p3 = "123456"; //123456\0在常量区,p3在栈上。
static int c =0; //局部静态变量 (全局/静态存储区)
p1 = (char *)malloc(10); //分配得来得10和20字节的区域就在堆区。
p2 = (char *)malloc(20);
strcpy(p1, "123456"); //123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
return 0;
}
一些问题
自由存储区和堆的区别:从技术上来说,堆(heap)是C语言和操作系统的术语,堆是操作系统所维护的一块特殊内存,它提供了动态分配的功能,使用malloc()、free()来申请/释放内存。而自由存储是C++中通过new和delete动态分配和释放对象的抽象概念。基本上,所有的C++编译器默认使用堆来实现自由存储。也就是说,默认的全局运算符new和delete也许会使用malloc和free的方式申请和释放存储空间,也就是说自由存储区就位于堆上。但程序员也可以通过重载操作符,改用其他内存来实现自由存储,例如全局变量做的对象池,这时自由存储区就不位于堆上了。因此,可以这样理解,堆是操作系统维护的一块内存,是一个物理概念,而自由存储是C++中通过new与delete动态分配和释放的对象的存储区,是一个逻辑概念。堆与自由存储区并不等价。
参考博客:
1.C++内存管理学习堆和栈 - 云+社区 - 腾讯云 (tencent.com)