C++内存结构分为代码区,数据区和栈区。代码区存放编译的机器指令;数据区又分为堆区和全局数据区,其中堆区存放malloc出来的内存,全局数据区存放全局变量和静态变量;栈区存放自动变量,由编译器分配和释放。
C/C++存储空间结构
BSS: Block Started by Symbol,意为“以符号开始的块”。在C语言中还分:未初始化的全局变量和静态变量、已初始化的全局变量和静态变量。但是在C++中没有这个区分,他们共同占用同一块内存区。代码区和数据区内存数据从可执行文件中读取。
- 代码区:存放CPU执行的机器指令,代码区是可共享,并且是只读的;
- 数据区(全局/静态存储区):存放已初始化的全局变量、静态变量(全局和局部)、常量数据;
- BBS区:存放未初始化的全局变量和静态变量;
- GVAR区:存放已经初始化的全局变量和静态变量;
- 栈区:由编译器自动分配释放,存放函数的参数值、返回值和局部变量,在程序运行过程中实时分配和释放,栈区由操作系统自动管理,无须程序员手动管理;
- 堆区:堆是由malloc()函数分配的内存块,使用free()函数来释放内存,堆的申请释放工作由程序员控制;
- 自由存储区:由new分配的内存块都是自由存储区,是C++中才有的抽象概念。自由存储区不是一个具体的物理概念,对应的物理存储区可能是堆区,也可能是静态存储区,需要看不同编译器new的实现方式。
new和malloc的区别参考new和malloc区别。
基本数据类型占据内存大小
32、64位编译器不同数据类型占据内存字节数如下:
数据类型 | 32位 | 64位 |
---|---|---|
char | 1 | 1 |
short | 2 | 2 |
int | 4 | 4 |
long | 4 | 8 |
long long | 8 | 8 |
指针 | 4 | 8 |
float | 4 | 4 |
double | 8 | 8 |
C++类和对象的存储
C++类的构成:
数据成员:非静态的内置类型、对象类型,静态数据成员
函数成员:非虚函数,虚函数
静态数据成员不与任何对象绑定,即不存在隐含的this指针,存放在堆区。静态成员函数不能声明成const(没有隐含的this指针),也不能在其内部使用this指针。
- 静态数据成员不占对象存储空间;
- 内置类型占据空间如上;
- 对象数据类型占据空间按照类内存对齐原则;
- 非虚函数不占内存;
- 虚函数存在虚函数指针占据内存。
类类型对齐原则是指在牺牲一定空间损失的情况下,换取内存读取次数的减少。详情参考C++类内字节对齐。
不保存函数成员的考虑:OOP编程其中一个目的就是代码重用,相同类不同对象的函数成员没有必要给每个对象保存,放置在公共区能节省内存。对象调用成员函数时通过域作用符和函数名便能寻址,然后把对象名传递给隐式的this指针便能分辨具体调用的是哪个对象。
虚函数通过虚表实现,包含虚函数的对象有一个指向虚表的指针,这个指针占据内存。
有关this的详细介绍参考this指针。
有关虚函数的详细介绍参考虚函数。
空白类对象占据1字节
是为了不同实例之间的区分。空白类也能实例化,为了区分每个对象需要具有不同地址,因此至少得分配1字节内存。在继承时,如果基类是空白类,那么子类继承父类的那1个字节会被优化掉,变为0,成为空白基类最优化,参见空白基类最优化。
例子
在win10 gcc32环境下类A占据内存8字节,类B占据内存24字节,类C占据内存32字节,类X占据内存8字节,如代码中注释。但在centos 64, gcc64环境下类A占据内存16字节,类B占据内存32字节,类C占据内存48字节,类X占据内存8字节。centos 64, gcc64环境的默认对齐系数也是8,但指针是8字节,造成了不同。
内存对齐会导致对象占据的空间比单个数据占据空间之和多,关于对象内存字节对齐,参考C++类内存对齐。对象成员内存分布的先后顺序会影响内存对齐,虚表指针始终是对象内存空间的首元素,关于C++对象虚表的存储模型,参考C++虚函数。排在第二的是从父类继承来的成员,最后是子类自己非继承、非虚表指针数据成员。
1 |
|