惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 司徒正美
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Last Week in AI
Last Week in AI
大猫的无限游戏
大猫的无限游戏
博客园 - Franky
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
爱范儿
爱范儿
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
博客园 - 叶小钗
博客园_首页
有赞技术团队
有赞技术团队
WordPress大学
WordPress大学
宝玉的分享
宝玉的分享
V
V2EX
V
Visual Studio Blog
博客园 - 三生石上(FineUI控件)
S
SegmentFault 最新的问题
量子位
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
美团技术团队

博客园 - 新西兰程序员

C++ 构造函数中的初始化参数列表initializer list C++中的线程thread,以及C++11中的std::atomic学习 Leetcode56 Merge Intervals 合并区间 -- C++实现 C++中的static关键字使用以及全局变量 C++17中的结构化绑定Structured Binding C++中的函数参数是指针类型, 是按值传递 C++中异常处理机制中的栈展开stack unwinding C++实现链表反转 Linux中的性能分析工具 perf 来分析C++性能 C++17中新建一个类时,编译器默认生成的类成员函数 QT中的元对象系统 Leetcode 114 - 二叉树展开为链表 Leetcode65 有效数字 - 判断给定的字符串是否是一个有效数字 LettCode2289-Steps to Make Array Non-descreasing C#中的委托详解 C++中的std::function C++中的仿函数Functor C++中的const和constexpr异同比较 C++中的左值和右值,以及右值引用,移动语义 C++中传递参数是指针类型以及传入参数是指针的指针(**)详解 C++中GetTickCount函数学习 C++中以类的成员函数作为Windows callback函数需要设置成static函数 C#中的System.Security.SecureString学习 C++中的悬挂指针和野指针 C++中四种不同的对象生存方式(in stack, in heap, global, local static) C#中使用Parallel类来进行多线程并发编程 String类型转LPCTSTR -----理解C++中的字符串类型转换 C++中的虚函数和虚函数表 C++中基类指针指向派生类对象 数据结构 - 栈的学习
C++中的内存对齐
新西兰程序员 · 2026-05-12 · via 博客园 - 新西兰程序员

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

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

性能提升 -- 减少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
  • 结构体 / 类对齐 = 内部最大对齐成员
  • 总大小必须是「自身对齐值」整数倍

关于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