C++中的内存对齐

在计算机科学中,内存对齐是一种数据存储方式,它要求某些类型的数据必须存储在特定地址上。这种要求是由硬件架构决定的,不同的处理器可能有不同的对齐要求

内存对齐的目的和作用: 性能提升和平台兼容性

性能提升 -- 减少CPU的访问次数,优化内存带宽利用

平台兼容性 -- 硬件限制和移植性

C++中的内存对齐原则

C++标准规定了每个数据类型的最小对齐要求,通常为该类型大小的倍数. 比如,int类型(假设为4字节)应该至少4字节对齐,double类型(假设为8字节)应该至少8字节对齐

基本数据类型:对于内置类型如 char, short, int, long, float, double等,它们的对齐方式通常是其自身大小

结构体和类:对于复合类型,像结构体和类,它的整体对齐要求是其所有成员中最大的那个,按照它的对齐大小进行对齐.

数组:数组的对齐方式和其元素类型相同

**特别注意**: 对齐数 = 编译器默认的一个对齐数与该成员大小的较小值

比如 Visual Studio的默认对齐数是8

 我们现在来看一个例子

#include <iostream>
#include <type_traits>

struct MyStruct
{
    char a; // 1 byte
    int b; // 4 byte
    double c; // 8 byte
    short d; // 2 byte
};

int main()
{
   // struct结构体中每个成员的偏移量
    std::cout << "offset of a is " << offsetof(MyStruct, a) << std::endl;
    std::cout << "offset of b is " << offsetof(MyStruct, b) << std::endl;
    std::cout << "offset of c is " << offsetof(MyStruct, c) << std::endl;
    std::cout << "offset of d is " << offsetof(MyStruct, d) << std::endl;


   //struct结构体中每个成员的内存对齐要求
   std::cout <<"Alignment of char " << alignof(a) << std::endl;
   std::cout <<"Alignment of char " << alignof(b) << std::endl;
   std::cout <<"Alignment of char " << alignof(c) << std::endl;
   std::cout <<"Alignment of char " << alignof(d) << std::endl;

  //struct结构体的大小和对齐要求
   std::cout << "size of MyStruct " << sizeof(MyStruct) << std::endl;
   std::cout << "Alignment of MyStruct " << alignof(MyStruct) << std::endl; 

   return 0;
}

 我们来看结构体的内存对齐原则

1. 每个成员必须在自身对齐大小的整数倍偏移地址

2. 结构体的整体对齐大小 = 结构体内部最大成员的对齐大小 (这里是 double = 8)

3.  结构体的总大小必须是整体对齐大小的整数倍,也就是结构体内部最大成员内存对齐大小的整数倍. => 这里也就是8的整数倍

 MyStruct的char型成员变量a

大小: 1

对齐: 1

偏移: 0

占用: 0 (占用偏移为0的位置)

MyStruct的int型成员变量b

对齐: 4  -必须放置在偏移为4的位置.  a只占0~1,所以要填充3字节(1,2,3)

偏移: 4

占用: 4-7

 MyStruct的double型成员变量c

对齐: 8 -必须放置在偏移为8的位置

偏移: 8

占用: 8-15

MyStruct的short型成员变量d

对齐: 2

偏移:16 (也就是d的起始位置)

占用: 16-17

结构体MyStruct的整体大小

  • 所有成员占用:0 ~ 17 → 18 字节
  • 结构体整体对齐 = 8 字节
  • 18 向上取 8 的倍数:24 字节
  • 最后填充:6 字节(18~23)
 
最终 sizeof (MyStruct) = 24

 我们再来看几个例子,以便加深理解

以下几个例子,默认64位系统,自然对齐

  • char=1 short=2 int=4 long/double/ 指针 = 8
  • 有虚函数:自带 8 字节 vptr
  • 结构体 / 类对齐 = 内部最大对齐成员
  • 总大小必须是「自身对齐值」整数倍
 例子1 - 类中带虚函数
关于C++中的虚函数,回顾的话可以看看我写的文章 https://www.cnblogs.com/wphl-27/p/18113373

 

class Test
{
    char a;
    int b;
    virtual void virFunc();


};

求 sizeof(Test);

 分析:

Test类中存在一个虚函数,它就会有一个8字节的指向虚函数表的指针vptr(注意,类中无论有多少个虚函数,都只有一个8字节的指针vptr) -> 具体可以参考我写的博客 https://www.cnblogs.com/wphl-27/p/18113373

vptr -> 0-7

char-> 8-9

int-> 占4位,所以必须是4的倍数开头,所以内存对齐填充 10,11

        int 是 12-15

所以总的内存大小是 0-15, 共16, 它是类的自身对齐值8 (类中内部最大成员vptr的占位大小)的倍数, 所以就算16

 

 例子2 - 单继承 + 虚函数

class A
{
    virtual void f1();
    char a;
};

class B : public A
{
    int b;
    double c;
};
// 求 sizeof(A)、sizeof(B)

sizeof(A)

一个虚函数有指针vptr -> 0-7

char a -> 7-8 

所以总的内存大小是 0-8, 共9, 它不是类的自身对齐值8 (类中内部最大成员vptr的占位大小)的倍数, 所以要补齐,成为8的倍数

补 9-15, 共16

所以 sizeof(A) = 16

 

sizeof(B)

继承A的成员

vptr -> 0-7

char a -> 7-8

int b -> 占4位,所以必须是4的倍数开头,所以内存对齐填充 9,10,11

             int 是 12-15

double c -> 16-23

所以总的内存大小是 0-23, 共24, 它是类的自身对齐值8 (类中内部最大成员vptr的占位大小)的倍数, 所以就算24

 

posted on 2026-05-12 14:58  新西兰程序员  阅读(12)  评论(0)    收藏  举报