C++类对象内存对齐方式

C++类的内存分配 内存对齐内存对齐的原因 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。 对齐规则每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员 阅读详情


在C++中,类的内存对齐方式遵循以下一些原则和特点:

一、基本概念

内存对齐(Memory Alignment)也叫字节对齐,是指数据在内存中的存放地址要满足一定的规则,通常是按照数据类型自身的对齐模数来进行对齐放置。这样做的主要目的是为了提高处理器访问内存的效率,因为处理器在读取内存时往往按照字长(比如32位系统按4字节,64位系统按8字节)等单位进行读取,合适的对齐能让数据一次性被完整读取,减少读取次数。

二、对齐规则

  1. 数据成员对齐
    对于类中的各个数据成员,编译器会按照它们各自类型对应的对齐要求来安排内存位置。常见基本数据类型的对齐要求如下:
    • char类型:通常按1字节对齐,也就是说char类型的数据可以存放在任意内存地址上,因为任何地址对于1字节的访问都是对齐的。
    • short类型:一般按2字节对齐,意味着short类型的数据的存储地址应该是2的倍数(例如在内存地址0、2、4等位置)。
    • int类型:多数情况下按4字节对齐,其存储地址需是4的倍数(像内存地址0、4、8等)。
    • double类型:常按8字节对齐,存储地址要是8的倍数(例如内存地址0、8、16等)。

例如,有这样一个类:

class AlignmentExample {
    char c;
    int i;
    short s;
};

按照对齐规则,首先char类型的c可以存放在任意地址(假设起始地址为0),然后int类型的i由于要按4字节对齐,所以它会从地址4开始存放(因为前面的c占了1字节,填充3字节使其对齐到4字节边界),最后short类型的s按2字节对齐,它会接着存放在地址8处(因为地址4 - 7被i占用了,地址8刚好满足2字节对齐要求)。整个类对象的大小可能会是10字节,但考虑到内存对齐,编译器通常会填充到12字节,使得下一个对象(如果有连续存放的情况)也能按照合适的对齐要求来放置。

  1. 继承情况下的对齐
    在继承关系中,派生类对象的内存布局会先包含基类的数据成员,并且遵循基类原有的对齐方式以及整个继承体系下的综合对齐要求。

比如:

class Base {
    int baseNum;
};

class Derived : public Base {
    short derivedNum;
};

Derived类对象的内存中,先是Base类的baseNum按4字节对齐存放,然后Derived类的short类型的derivedNum按2字节对齐存放在合适位置(在baseNum之后,且满足2字节对齐要求)。

  1. 结构体(类)整体对齐
    一个类或者结构体整体的大小也需要满足其内部最严格的对齐要求。也就是说,类或结构体的总大小应该是其内部最大对齐模数(通常是最大数据类型对应的对齐字节数)的整数倍。例如,如果一个类中有int(按4字节对齐)和double(按8字节对齐)类型的数据成员,那么这个类整体的大小要为8的倍数,编译器会在必要时填充一些字节来达到这个要求。

三、编译器控制对齐

  1. 指定对齐方式(以#pragma pack为例)
    可以通过编译器指令来改变默认的对齐规则,比如在一些编译器中使用#pragma pack指令。

例如:

#pragma pack(1)
class PackedClass {
    char c;
    int i;
    short s;
};
#pragma pack()

#pragma pack(1)表示设置按照1字节对齐,此时PackedClass类对象就不会有额外的填充字节来满足其他对齐要求了,它的大小就是char(1字节) + int(4字节) + short(2字节) = 7字节。而#pragma pack()用于恢复编译器的默认对齐设置。

不同的编译器可能还支持其他方式来指定对齐参数,比如有些编译器可以使用特定的编译选项来全局设置对齐规则等。

  1. 属性指定(部分编译器支持)
    有些编译器允许通过属性(Attributes)来指定类或者数据成员的对齐方式,例如在某些支持特定扩展的C++编译器中,可以像下面这样做:
class __attribute__((aligned(8))) AlignedClass {
    // 类中的数据成员
};

上述代码通过__attribute__((aligned(8)))属性指定AlignedClass类按照8字节对齐,类中的数据成员以及整体的大小都会遵循这个8字节对齐的要求来布局和确定。

总之,C++类的对齐方式是综合考虑数据成员类型、继承关系以及编译器相关设置等多方面因素的,合适的对齐有助于提升程序运行时内存访问的性能。

1、结构体大小由成员变量和偏移量相加而成;
2、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处,整体大小也要能被对齐数整除

总对齐数: = 编译器默认的一个对齐数与该成员大小的较小值, 也就是min{编译器默认的对齐数, 成员变量大小的最大值},VS中默认的对齐数为8;

单个对齐数:该类型变量所占内存大小,char 对齐数是1 ,int 对齐数是4 , double 是8 , 32位指针类型是4 , 64位指针类型是8;

对齐原则分两步:
1、单个成员变量对齐(也就是说,单个变量的起始地址能被单个对齐数整除)
2、整体对齐(整体大小能被对齐数整除,若不能不整除,则增加偏移来填充)

相同的成员变量,因为声明位置不同,导致占用内存空间不同,看下面代码

void test6()
{
//总对齐数min{编译器默认的对齐数=8 ,成员变量大小的最大值double=8 } = 8
struct node
{
//假设总体空间为all
double m;//8 double对齐数为8 ,此时all=8 [0:7]
int a;//4 int 对齐数为 4 all=12 [8:11]
char c;//1 char对齐数 1 all=13 [12]
char b;//1 char对齐数 1 all=14 [13]
};//16 开始整体对齐,要能被整体对齐数整除,也就是能被8整除,14不能被8整除,+2=16 即可,所以all=16

struct node1
{
	char c;		//1	 char对齐数  1         all=1
	int a;//4	       int对齐数  4  (char c 后空出三个字节,从第四个字节开始存放 int a)       all=8
	char b;			//1  char对齐数  1         all=9
	double m;//8    double对齐数为8  (char b 后空出7个字节,从第四个字节开始存放double m) ,此时all=24
};//24     24能被8整除,所以整体不需要偏移

cout << sizeof(node) << "---" << sizeof(node1) << endl;

}

单个成员变量对齐(也就是说,单个变量的起始地址能被单个对齐数整除)更加通俗的讲就是;double类型必须从被8整除的地址开始,int 必须从被4整除的地址开始,若起始地址不能被整除,则向后移动,直到能被整除,空出来的空间直接浪费,用空间换时间。

如果嵌套了结构体的情况,内层嵌套的结构体成员对齐到自己的最大对齐数的整数倍的地址,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

为什么要偏移
平台的原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取到某些特定类型的数据,否则抛出硬件异常。
性能的原因: 数据结构(尤其是栈)应该尽可能的在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

C++:对齐方式 C++:对齐方式C++:对齐方式对齐和内存地址数据对齐的编译器处理alignof 和 alignas示例 C++:对齐方式 C + + 的低级功能之一是能够指定内存中对象的精确对齐方式,以最大限度利用特定的硬件体系结构。 默认情况下,编译器会按大小值对类和结构成员进行对齐:在1个字节的边界上、在2字节边界上、 bool char short int 、和 long 4 字节 float long long double 边界上、、以及 long double 8 字节边界上。 在大多数情况下,你永远不需要关 阅读详情

相关推荐

细说C++中字节的对齐方式

C++中字节的对齐方式可以采用编译器默认的字节对齐方式, 也可以采用自定义的对齐方式。 编译器默认字节对齐方式 1、结构体中每个成员分别按自己的方式对齐(注:对于数组如int a[3], 其对齐字节为sizeof(int)) 2、复杂类型(包含结构体成员)的默认对齐方式是它最长的成员的对齐方式,这样的成员是复杂类型时,以其最长的成员的对齐方式对齐。 3、对齐后的长度必须是该类型的对齐字节数的整数倍。

不带这样的专栏 6532

内存对齐原则

内存在使用过程并不是单一的依次排列,而是按照某种既定的规则来进行对齐,以方便快速访问.内存的对齐原则有以下三条: 数据成员对齐:成员根据其自身大小,从自身大小的整数倍内存地址(以第一个元素存储在0位置为参考)开始存储; 结构体成员对齐:如果包含了结构体成员,则结构体成员的存储位置从其内部成员最大值的整数倍地址开始存储; 结构体总大小对齐:必须要是其内部最大成员的整数倍,不足的要补齐. 注:以...

王二东的博客 3123

C++11中使用sizeof获取类成员大小

而sizeof(m_i)的结果为4字节,是int类型的大小,而sizeof(m_d)的结果为8字节,是double类型的大小。首先,需要明确的是,对于非静态成员变量,sizeof运算符只会计算其类型的大小,而不会计算实际占用内存的大小。具体来说,sizeof运算符计算非静态成员变量的大小时,只会考虑成员变量的类型和对齐方式,而不会计算该成员变量实际所占用的内存大小。但是,需要注意的是,sizeof运算符只能计算类型的大小,而不能计算实际占用内存的大小,在具体使用时需要谨慎处理。

pytorchCode的博客 285

【C/C++】内存对齐(超详细,看这一篇就够了)

下面的结论均为系统默认对齐规则下进行的: 通常情况:我总结的规律如下:结构体中间:各结构体的起始地址按照各个类型变量默认规则进行摆放,但除了 char 类型变量(详见一),char 类型变量一般遵循 2 的倍数地址开始存储。详见例2。结构体最后(重要):视结构体中最大类型是哪一个,如果是像 int 类型那样是 4 个字节的,并且结构体的结尾地址不满足 4 的倍数的话,向离最近的 4 的倍数地址补齐;如果是像 double 类型那样是 8 个字节的,并且结构体的结尾地址不满足 8 的倍数的话,向离最近的 8

涛涛子的博客 3万+

Crossbar RRAM技术解析:从1TnR架构到存储革命

非易失性存储器(NVM)是数据存储的核心技术,其原理基于物理状态的稳定改变以实现数据持久化。阻变存储器(RRAM)作为一种新兴NVM,利用导电细丝在介质层中的形成与断裂实现数据存储,具有高速、低功耗和结构简单的优势。在工程实践中,RRAM面临潜行路径电流等挑战,而Crossbar提出的1TnR架构通过引入共享晶体管有效解决了这一问题,在保证性能的同时提升了集成密度。该技术与CMOS工艺兼容,支持后端集成与多层堆叠,为高密度存储和嵌入式应用提供了新的解决方案。结合当前行业对高能效、高密度存储的需求,RRAM在

weixin_42523104的博客 322

JOOQ用法和实例

jOOQ,是一个ORM框架,利用其生成的Java代码和流畅的API,可以快速构建有类型约束的安全的SQL语句。jOOQ的核心优势是可以将数据库表结构映射为Java类,包含表的基本描述和所有表字段。通过jOOQ提供的API,配合生成的Java代码,可以很方便的进行数据库操作生成的Java代码字段类型是根据数据库映射成的Java类型,在进行设置和查询操作时,因为是Java代码,都会有强类型校验,所以对于数据的输入,是天然安全的,极大的减少了SQL注入的风险。

m0_67403076的博客 1378

内存对齐规则总结

内存对齐的详细规则总结(包括具体实例代码)

weixin_43487974的博客 4233

MCP4728 电路及stm32F103程序移植

写在前面 通过优快云查到一些MCP4728 的程序,结合程序及datasheet对于驱动的描述,理清了思路 感谢开路者! MCP4728的电路比较简单,见下文描述 应用电路 datasheet: 实际测试电路: SCL接stm32的PB10,SDA接PB11 RDY/BSY引脚和LDCA引脚,悬空 解释见截图: MCP4728地址 选择任意输出通道写入DAC输入寄存器 应用程序 int mcp4728_write_all_channel(float vol1, float vol2, fl

u013074298的专栏 5428

关于内存字节对齐

<br />1:数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储。<br />2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,doub

flower_fancy的专栏 1001

从拧螺丝到精密装配:三菱伺服电机扭矩控制在工业自动化中的实战解析

本文深入解析三菱伺服电机扭矩控制在工业自动化中的实战应用,重点介绍MR-J4系列伺服系统的高精度扭矩响应技术。从螺丝紧固到精密装配,详细讲解扭矩控制模式的选择、硬件配置、参数设置及典型故障排查方案,帮助工程师提升生产效率和产品质量。

weixin_29186249的博客 471

提示libfakeroot.so或libfakeroot-sysv.so出错处理方法

嵌入式Linux、Buildroot交叉编译出现fakeroot问题处理方法。

阿xin*的嵌入式AI 1445

史上sizeof类所有性质最全面总结—预处理、const、static与sizeof(三)

史上sizeof类所有性质最全面总结

恋蛩音的博客 792

【AI】获取各类AI大模型api-key的快速教程(OpenAI、Gemini、Claude、DeepSeek)

【AI】获取各类AI大模型api-key的快速教程(OpenAI、Gemini、Claude、DeepSeek)

funfan的博客 3019

C++结构体与类对齐

struct tag { member-list }variable-list; 注:struct为结构体关键字; tag为结构体的标志; member-list为结构体成员变量列表,其必须列出其所有成员; variable-list为此结构体声明的变量; //此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c,但没有标明其标签,声明了结构体变量...

萌二三 607

PCL 计算点云包围球【2025最新版】

包围球通常用于简单排斥,因为与球体相交的方程很简单,同样重要的是,旋转一个球体并不会改变其范围。因此,当包围球用于简单排斥时,如果球体中心是对象的原点,则可以忽略对象的方向。

点云侠的博客 2068

华为OD技术面真题 - 数据库Mysql - 1

汇总华为OD技术面高频Mysql相同问题,提供详细解答。

qq_45776114的博客 1086

关于内存对齐问题(一)

a. 基本类型:所有的基本类型都有相应的对齐参数,编译器在编译时,会用全局的对齐参数和当前类型的对齐参数中较小的一个进行对齐。比如,编译时指定暗8bytes对齐(用#pragma pack(8)实现之),可是由于一个char变量的大小为一个byte,所以最后还是按1byte对齐。 b. 复合类型:复合类型的对齐原则,就是取其成员变量数据类型的字节数的最大者和在编译时指定的对齐数两者之间较

Lostyears的专栏 638

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

C++ 中字节对齐总结

C++ 中字节对齐总结

编程猿来如此 985

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

一些小记录

看侯捷大佬的内存管理时的一点记录。 operator new/delete 全局的重载 ::operator new/new[]/delete/delete[]可以重载,但是不能在命名空间内重载。重载之后,使用new的时候会调用::operator new。如果在类里重载了operator new,则new 类的对象时会调用类里的函数。 #include<iostream> using namespace std; void* operator new(size_t size) { c

gamebyc的博客 129

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

第四章 数据语义学

1、数据成员绑定时机 2、进程内存空间布局 3、数据成员布局 3.1 观察成员变量地址规律 class MYACLS { public: int m_i; static int m_si; //声明不是定义 int m_j; static int m_sj; int m_k; static int m_sk; }; int MYACLS::m_sj = 0; //此为定义 int main() { MYSCLA myobj; // sizeof(myobj) = 3 * siz

qq_35763737的博客 925

c语言sizeof怎么计算位域,详解C++ sizeof(下)

sizeof作用于基本数据类型,在特定的平台和特定的编译器中,结果是确定的,如果使用sizeof计算构造类型:结构体、联合体和类的大小时,情况稍微复杂一些。1.sizeof计算结构体考察如下代码:struct S1{char c;int i;};cout<sizeof(S1)结果是8,并不是想象中的sizeof(char)+sizeof(int)=5。这是因为结构体或类成员变量具有不同类型时...

weixin_42407739的博客 756

C++类的存储及类对象内存结构

本文分两部分,前半部分讲类的存储后半部分讲类的内存结构。 C++类的存储 c++中最重要的就是类,那么一个类的对象,它在内存中如何存储的?它占 内存中多少个字节? 首先确定类的构成: 1,数据成员:可以是内置类型,类类型。 2,函数成员:虚函数,非虚函数 1)数据成员 内置类型对齐原则 内置类型就是常用的:char,short,long,int,float,double. 这些内

fenxinzi557的博客 2万+

C++类对象的内存结构

本文全文转载于:https://blog.youkuaiyun.com/fenxinzi557/article/details/51995911 (1) 无虚函数继承 (2)有一个虚函数继承 (3)全部虚函数都继承 (4)多重继承 (6)菱形继承 (7)单一虚继承 (8)菱形虚继承(最重要) 结论: 在VS中查看类对象的内存布局的方法:右击源文件——>属性—...

蓬莱道人的博客 5320

C++中类对象所占用的内存空间

本文主要介绍C++中类对象的内存分布。

TechNomad的博客 783

类对象和类指针以及类的内存分布

类对象和类指针是两个概念 比如有个类:class Test; 定义类对象:Test test; 1.test是类Test的一个实例, 2. test是类Test的一个实例,可以供通过"."运算符访问类中的public成员 定义类指针:Test *p_test; // 此时p_test只是一个指向Test类类型的指针,还没有分配内存。要使用指针,需要p_test = new Test(); ...

积累点滴,保持自我 1015

C++之:类对象内存对齐

一、关于空间的思考一个Class对象需要占用多大的内存空间。最权威的结论是: *非静态成员变量总合。(not static) *加上编译器为了CPU计算,作出的数据对齐处理。(c语言中面试中经常会碰到内存对齐的问题) *加上为了支持虚函数(virtual function),产生的额外负担。 二、例程//注:以下注释是在mac os g++ 环境编译:g++ memAlign

This is bill的专属博客 2053
上一篇: C++ 空类大小
下一篇: C语言中的强弱符号
越甲八千
博客等级 码龄11年 2959粉丝 553原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值