内存对齐

C++空类的内存大小为1字节,为了保证其对象拥有彼此独立的内存地址。非空类的大小与类中非静态成员变量和虚函数表的多少有关(如果有虚函数就+4字节,无论有几个,虚函数可以看做是一个4字节的非静态成员变量,而且应该也是看虚函数出现位置的)。值得注意的是,类中非静态成员变量的大小与编译器内存对齐的设置有关。成员变量在类中的内存存储并不一定是连续的。它是按照编译器的设置,按照内存块来存储的,这个内存块大小的取值,就是内存对齐。

为什么要对齐?

1、访问未对齐的内存,处理器要访问两次(数据先读高位,再度地位),访问对齐的内存,处理器只要访问一次,为了提高处理器读取数据的效率,我们使用内存对齐。Windows 默认对齐数为8字节,Linux 默认对齐数为4字节。

2、使用内存对齐的原因还有平台的原因:不是所有的硬件平台都能访问特定的地址上的任意数据,某些平台只能访问特定的地址上的获取数据,否则会抛出异常,遇到未对齐的边界直接就不进行读取数据了。

 

对齐规则

【对于整体】

1、第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。

2、在数据成员完成各自对齐之后,类(结构)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构最大数据成员长度中,比较小的那个进行。

3、#pragma pack(n) 作为一个预编译指令是用来设置多少个字节对齐的。值得注意的是,n的缺省数值是按照编译器自身设置,一般为8,合法的数值分别是1、2、4、8、16,n为其他值无效。

【对于类(结构体)】

1、分配内存的顺序是按照声明的顺序。

2、每个变量相对于起始位置的偏移量必须是该变量类型大小的整数倍,不是整数倍空出内存,直到偏移量是整数倍为止。

3、最后整个结构体的大小必须是里面变量类型最大值的整数倍。

 

范例

#include<iostream>
using namespace std;

class test {
private : 
    char c='1';//1byte 
    int i;//4byte
    short s=2;//2byte
};
class test2 {
private:
    int i;//4byte
    char c = '1';//1byte 
    short s = 2;//2byte
};

int main(){
    cout << sizeof(test) << endl;
    cout << sizeof(test2) << endl;
    return 0;
}

输出为:
12
8

对于类test的内存空间是这样的:

内存分配过程:

1、char和编译器默认的内存缺省分割大小(8)比较,char比较小,分配一个字节给它。

2、int和编译器默认的内存缺省分割大小比较,int比较小,占4字节。因为如果紧挨着刚才的字节,起始位置的偏移量就是1,不是4的整数倍,所以只能空3个字节,重新分配4个字节。

3、short和编译器默认的内存缺省分割大小比较,short比较小,占2个字节,分配2个字节给它。

4、对齐结束类本身也要对齐,类里面变量的最长字节数为4,比8小,所以最后要4字节对齐,所以最后空余的2个字节也被test占用。

对于类test2的内存空间是这样的:

1、int和编译器默认的内存缺省分割大小比较,int比较小,占4字节。分配4个字节给int。

2、char和编译器默认的内存缺省分割大小比较,char比较小,分配一个字节给它。

3、short和编译器默认的内存缺省分割大小比较,short比较小,占2字节,但现在offset是5不是2的正数倍,所以要空一个字节再放s。

4、因为结尾的offset正好是类成员最大值4的整数倍,所以不用调整。

 

技巧

当然上面只是编译器默认的分配规则,我们可以通过下面几个方法改变结构体的大小

1、改变结构体中变量的声明顺序,按照类型从小到大的顺序声明,占用的空间就会比较小。

2、可以使用#pragma修改这个规则,#pragma是C++的一个预处理指令,它有很多作用,其中一个作用就是修改分配规则。

在上面代码的结构体定义前面添加:

#pragma pack(1)    //设定为1字节对齐

这样上面的test和test2占用的内存就都是7了,因为是1字节对齐,所以每次的offset肯定都是符合要求的,不用空出字节,而且最后对于整个类的对齐,无论类中成员的最长字节是多少,反正要和1取小的那个,所以最后也不用空。

添加了 #pragma pack(n) 后规则就变成了下面这样:

1、偏移量要是n和当前变量大小中较小值的整数倍

2、整体大小要是n和最大变量大小中较小值的整数倍

3、n值必须为1,2,4,8…,为其他值时就按照默认的分配规则

 

补充:union大小计算

union U { 
    char s[9];
    int n;
    double d;
}; 

s占9字节,n占4字节,d占8字节,因此其至少需9字节的空间。然而其实际大小并不是9, 用运算符sizeof测试其大小为16。

这是因为这里存在字节对齐的问题,9既不能被4整除, 也不能被8整除。因此补充字节到16,这样就符合所有成员的自身对齐了。 从这里可以看出联合体所占的空间不仅取决于最宽成员,还跟所有成员有关系, 即其大小必须满足两个条件:

  • 大小足够容纳最宽的成员;
  • 大小能被其包含的所有基本数据类型的大小所整除。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值