赞
踩
说明:在C++中,内存布局(Memory Layout)是指程序中的数据结构(如类、结构体、联合体等)在内存中的排列和存储方式。它包括数据成员的顺序、大小以及它们之间的对齐和填充情况。内存布局对程序的性能有直接影响,因为它决定了数据访问的效率和内存的使用情况。
接下来我们看看C++11之前的布局和C++11之后的布局有什么差异。
C++11之前的内存布局:
C++11内存布局的变化:
总之,C++11在内存布局方面提供了更多的控制和标准化,使得开发者能够更精确地管理数据结构在内存中的排列,同时也提高了代码的可移植性和性能。
接下来我们详细解读为什么引入内存布局。
C++中的内存布局对于程序的执行至关重要,原因包括但不限于以下几点(实际上和内存对齐的原因基本上是一致的):
内存布局是C++对象模型的核心组成部分,它直接关系到程序的运行时行为和性能。合理的内存布局可以提高程序的可移植性、性能和安全性。
内存布局在C++中的相关代码主要体现在以下几个方面:
以下是一些演示这些方面的代码示例。
参考代码如下:
- #include <iostream>
-
- struct SimpleStruct {
- char a; // 占用1字节
- int b; // 占用4字节,可能会有3字节填充以满足4字节对齐
- double c; // 占用8字节,可能会有4字节填充以满足8字节对齐
- };
-
- int main() {
- std::cout << "Size of SimpleStruct: " << sizeof(SimpleStruct) << std::endl;
- return 0;
- }
sizeof(SimpleStruct)
将输出SimpleStruct
的大小,这将包括成员变量的大小加上编译器可能插入的填充字节。对齐后输出为16。
参考代码如下:
- struct WithVTable {
- virtual void func() {}
- };
-
- int main() {
- std::cout << "Size of WithVTable: " << sizeof(WithVTable) << std::endl;
- return 0;
- }
sizeof(WithVTable)
将输出包含虚函数的WithVTable
的大小,这通常比不包含虚函数的结构体大,因为需要额外的空间来存储指向vtable的指针。因包含一个指向虚函数表的指针,通常是4或8字节(取决于系统架构)。
参考代码如下:
- struct Base {
- char baseMember;
- };
-
- struct Derived : Base {
- int derivedMember;
- };
-
- int main() {
- std::cout << "Size of Derived: " << sizeof(Derived) << std::endl;
- return 0;
- }
sizeof(Derived)
输出的是派生类Derived
的大小,它将包括基类Base
的成员和派生类自己的成员,以及可能的填充字节。在32位系统上,一个int
通常是4字节,所以Derived
结构体的总大小可能是1(Base::baseMember)+ 3(填充)+ 4(Derived::derivedMember)= 8
字节。同理,在64位系统上,考虑到更大的对齐边界,填充可能会不同,但一般情况下,由于int
和char
都是较小的类型,总大小可能仍然是8字节,除非编译器为了优化内存对齐而采用了更大的填充。
参考代码如下:
- struct VirtualBase {
- char baseMember;
- };
-
- struct DerivedVirtual : virtual Base {
- int derivedMember;
- };
-
- int main() {
- std::cout << "Size of DerivedVirtual: " << sizeof(DerivedVirtual) << std::endl;
- return 0;
- }
sizeof(DerivedVirtual)
将输出虚继承自Base
的DerivedVirtual
的大小,它将包括虚基类指针(vbptr)和虚基类表(vbtable)的额外空间。输出的分析如下:
1(Base::baseMember)+ 3(可能的填充)+ 4(vbptr)+ 4(DerivedVirtual::derivedMember)+ 3(可能的填充)= 15
字节,但实际大小可能会因为对齐而减少。1(Base::baseMember)+ 7(可能的填充)+ 8(vbptr)+ 4(DerivedVirtual::derivedMember)+ 4(可能的填充)= 24
字节。alignas
指定对齐参考代码如下:
- struct AlignedStruct {
- alignas(16) int alignedInt;
- };
-
- int main() {
- std::cout << "Size of AlignedStruct: " << sizeof(AlignedStruct) << std::endl;
- return 0;
- }
alignas(16)
指定alignedInt
成员需要16字节对齐,sizeof(AlignedStruct)
将反映出这一点,可能会有填充字节被插入以保持对齐。int alignedInt
本身占用4字节,但由于alignas(16)
,整个结构体将被填充至16字节对齐。
参考代码如下:
- struct WithStaticMember {
- static int staticMember;
- };
-
- int WithStaticMember::staticMember = 10;
-
- int main() {
- std::cout << "Size of WithStaticMember: " << sizeof(WithStaticMember) << std::endl;
- return 0;
- }
尽管WithStaticMember
包含一个静态成员,sizeof(WithStaticMember)
将输出非常小的值,因为静态成员不属于对象的内存布局,它们有单独的存储空间。因为静态成员staticMember
不包含在对象的内存布局中,所以sizeof
通常只反映出编译器可能插入的一个字节以确保对象有一个唯一的地址。
这些示例展示了C++中内存布局的不同方面,以及编译器如何通过插入填充字节和使用指针来管理内存布局和对齐。实际的内存布局和大小可能会因编译器和平台的不同而有所差异。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。