内存对齐问题

结构体大小
与数组不一样的是,结构体大小不是所有成员大小的简单相加,需要考虑系统在存储结构体变量时的地址对齐问题

struct stu1{
 int i;
 char c;
 int j;
 }

用sizeof求该结构体大小,发现值为12,int占4字节,char占1字节,应该是9字节才对,为什么呢

先介绍一个相关的概念–偏移量,偏移量指的是结构体变量中成员的地址和结构体变量地址的差,结构体大小等于左后一个成员的偏移量加上最后一个成员的大小,显然,结构体变量中的第一个成员的地址就是结构体变量的首地址,因此第一个成员i的偏移量为0.第二个成员c的偏移量是第一个成员的偏移量加上第一个成员的的大小(0+4),其值为4,第三个成员j的偏移量时第二个成员的偏移量加上第二个成员大小(4+1),其值为5
然而,在实际中,存储变量时地址要求对齐,编译器在编译程序时会遵守两条原则
1.结构体变量中的成员的偏移量必须是成员大小的整数倍(0被认为时任何数的整数倍)
2.结构体大小必须是所有成员大小的整数倍,也即所有成员大小的公倍数

上面的例子中,前两个成员的偏移量都满足要求,但第三个成员的偏移量是5,并不是自身int大小的整数倍,编译器在处理时会在第二个成员之后补上3个空字节,使得第三个成员的偏移量变成8,结构体大小等于最后一个成员的偏移量加上其大小,上面的例子中计算出来的大小是12,满足要求

对于嵌套的结构体将其展开,求结构体sizeof时上述两种原则变成:
展开后的结构的的第一个成员的偏移量应当是被展开的结构体中最大成员的整数倍
结构体大小必须是所有成员大小的整数倍,这里所有成员计算的是展开后的成员,而不是将嵌套的结构体当做一个整体

struct stu5 {
    short i; //2
    struct {
        char c;//1
        double j;//8
    } ss;
    int k;//4
};

这里的结构体大小就是32

没有成员的结构体占用的空间是多少个字节
一个字节,这就是实例化的原因,空类同样可以被实例化,每个实例在内存中都有独一无二的地址,为了达到这个目的,编译器往往会给一个空类或空结构体(C++结构体也可以看为类)隐含的加一个字节,这样的空类或者空结构体在实例化后在内存中得到了独一无二的地址,所以空类所占的内存大小是1字节

类大小的计算
计算规则:
1.遵循结构体的对其原则
2.与普通成员变量有关,与成员函数和静态成员函数无关,即普通成员函数,静态成员函数,静态数据成员,静态常量数据成员均对类的大小无影响,因为静态数据成员被类的对象共享,并不属于哪个具体的对象
3.虚函数对类的大小有影响,是因为虚函数表指针的影响
4.虚继承对类的大小有影响,是因为虚基表指针带来的影响
5.空类的大小是一个特殊情况,空类的大小为1,当用new来创建一个空类的对象时,为了保证不同对象的地址不同,空类也占用存储空间

class A {
private:
    const int c_var; //4
    int var;         //4
    char var1;       //1+3
public:
    A(int temp):c_var(temp){} //0
};

大小是12字节

带虚函数的情况:(虚函数的个数并不影响所占内存的大小,因为类对象的内存中只保存的指向虚函数表的指针)


class A {
private:
    const int c_var; //4
    int var;         //4
    char var1;       //1+3
public:
    virtual void f() { cout << "A::f" << endl; }
    virtual void g() { cout << "A::f" << endl; }//4字节
};

内存对齐:编译器将程序中的每个”数据单元“安排在字的整数倍的地址指向的内存中
内存对齐的原则
1.结构体变量的首地址能够被其最宽的基本类型成员大小与对齐基数中的较小者所整除
2.结构体每个成员相对于结构体首地址的偏移量,都是该成员大小与对其基数中的较小者的整数倍,如有需要,编译器会在成员函数间填充字节
3.结构体的总大小为结构体最宽基本类型成员大小与对齐基数中的较小者的整数倍,如有需要编译器会在最末一个成员之后加上填充字节

内存对齐的原因
1.某些硬件设备只能存取对其数据,存取非对齐数据可能会引发异常
2.某些硬件设备不能保证在存取非对其数据的时候的操作是原子操作
3.相比于存取对齐的数据,存取非对齐的数据需要花费更多时间
4.某些处理器虽然支持非对齐数据的访问,但会引发对齐陷阱
5.某些硬件设备只支持简单数据指令非对齐存取,不支持复杂数据指令的非对齐存取

内存对齐的优点
1.便于在不同的平台之间进行移植,因为有些硬件平台不能够支持对任意地址的数据访问,只能在某些地址处取某些特定的数据,否则会抛出异常
2.提高内存的访问效率,数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问,而对齐的内存访问仅需要一次

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值