内存对齐规则就不赘述了,这里讨论下继承和结构体作为另一个结构体成员变量的情况下,结构体内如何进行内存对齐。以下测试基于iOS 64位环境(小端),对齐系数设置为8进行,请忽略测试中堆中指针指向栈中数据的安全问题。
先上结论:
1,不论是继承还是作为另一个结构体成员变量,计算有效对齐值时,用的是所有使用到的结构体中占用空间最大的那个变量所占的内存来计算。
/*----------------------------------------举例情况1----------------------------------------*/
#pragma pack(8)
struct Super {
short a;
char b;
short c;
}; //有效对齐值为2
struct Sub : Super {
char *d;
}; // 有效对齐值为8
#pragma pack()
/*----------------------------------------举例情况2----------------------------------------*/
#pragma pack(8)
struct Super {
int a;
char b;
short c;
}; //有效对齐值为4
struct Sub : Super {
char d;
}; // 有效对齐值为4
#pragma pack()
2,继承的情况下,父类进行内存布局时,父类中的成员变量,按照父类中的对齐规则进行布局。子类进行内存布局时,把父类对齐过后所占的内存空间当作一个整体(不可以再对父类内存布局进行修改),再与子类中定义的成员变量,根据内存对齐规则进行布局。
/*----------------------------------------举例情况1----------------------------------------*/
#pragma pack(8)
struct Super {
short a;
char b;
short c;
}; //有效对齐值为2
struct Sub : Super {
char *d;
}; // 有效对齐值为8
#pragma pack()
image.png
3,作为另一个结构体成员变量的情况和继承类似,例如现在有a、b两个结构体,b中有一个成员变量为a类型。在b进行内存对齐时,把a对齐过后所占的内存空间当作一个整体,再与b中其他成员变量,根据内存对齐规则进行布局。
从设计上来理解:
1,编译器肯定是以预设的数据类型来计算有效对齐值的,我们自己写的结构体,取其中预设的数据类型来计算。如果按照我们自己写的结构体大小来计算有效对齐值,那么绝大部分情况计算出来都与系统的对齐系数相等。
2,我们自己写的结构体(a),如果放到其他结构体(b)中使用,a所占空间是不能改动的。如果改动了,会造成单独使用a和在b中使用a时,a占用的空间不一致。例如下面的例子,单独使用Super的时候,Super占4个字节。如果Sub对齐的时候结合Super的成员变量重新对齐,那么对齐后Sub所占用的内存应该为4个字节,Super中的变量占3个字节,Sub中新定义的变量占1个字节,这样前后矛盾了。实际打印结果为6个字节。
image.png image.png image.png
继承同理:
image.png image.png
测试:(以继承为例,结构体作为另一个结构体成员变量的情况类似)
maxSuper = 父类中占内存最大的那个成员变量所占内存
maxSub = 子类中占内存最大的那个成员变量所占内存
网友评论