iOS - 结构体内存分配

问题来源面试题:CGPoint在内存中如何分配?

CGPoint在OC中是一个结构体,结构体一般采用内存对齐的方式分配。
1、结构体每个成员相对于结构体首地址的偏移量都是这个成员大小的整数倍,如果有需要,编译器会在成员之间加上填充字节。
2、结构体的总大小为结构体最宽成员大小的整数倍。
3、结构体变量的首地址能够被其最宽基本类型成员的大小所整除。
4、对于结构体成员属性中包含结构体变量的复合型结构体,在确定最宽基本类型成员时,应当包括复合类型成员的子成员。但在确定复合类型成员的偏移位置时则是将复合类型作为整体看待。
5、总结:结构体的大小等于最后一个成员的偏移量加上其大小再加上末尾的填充字节数目,即:sizeof(struct) = offsetof( last item) + sizeof (last item) + sizeof( trailing padding)

例1:

struct fs{
    int i;
    char c;
    int x;
};

第1个成员相对结构体首地址的偏移量为0,是成员int i(长度为4)的整数倍。
第2个成员相对结构体首地址的偏移量为4,是成员char c (长度为1)的整倍。(因为结构体总大小为结构体最宽成员大小的整数位,所以如果此结构体只有这两个成员的话,会在char c后添加3个填充字节,但现在有3个成员,所以不需要填充。)
第3个成员相对结构体首地址的偏移量为5,不是成员int x 的整数倍,所以在x前(或者说是c之后)填充3个字节,以使x的偏移量达到8而成为4的整数倍。所以这个结构体占内存大小为4+1+3+4。

例2:成员个数与每个成员类型都一个,只不过顺序不一样,占内存大小就不一样。

struct fang {
    char a;     //补0   偏0   长1
    int i;      //补3   偏4   长8
    double d;   //补0   偏8   长16
};              //总长 16

struct fang1 {
    char a;     //补0   偏0   长1
    double d;   //补7   偏8   长16
    int i;      //补0   偏16  长20   20不是8整数倍,补4
};              //总长 24

例3:复合型结构体

struct dou {
    int a;      //补0   偏0   长4
    CGFloat b;  //补4   偏8   长16     16是8整数倍
};              //总长 16
typedef struct dou dou;

struct dou1 {
    char a;     //补0   偏0   长1
    dou dou;    //补7   偏8   长24     24是8整数倍,
};              //总长 24
typedef struct dou1 dou1;

对齐设置

#pragma pack(n) //编译器将按钮N个字节对齐,设置结构体最宽成员大小(与实际最宽成员大小取小)。即结构体最终长度是n的整数倍。
#pragma pack() //取消自定义对齐方式。
#pragma pack(puch,1) //把原来对齐方式保存起来,并设置新的对齐方式。
#pragma pack(pop) //恢复之前保存的的对齐状态
Expected #pragma pack parameter to be '1', '2', '4', '8', or '16'
预期的#pragma pack参数为'1','2','4','8'或'16'

#pragma pack(push)
#pragma pack(1)
//相当于
#pragma pack(puch,1)

demo

struct A{           //结构体长度按成员最大长度(8)对齐
    char A2;        //补0   偏0   长1
    long A1;        //补7   偏8   长16
    int A3;         //补0   偏16  长20   20不是8整数倍,补4
};                  //总长24
typedef struct A A;

#pragma pack(1)     //设置结构体长度按1对齐
struct B{
    char A2;        //补0   偏0   长1
    long A1;        //补0   偏1   长9
    int A3;         //补0   偏9   长13   13是1整数倍
};                  //总长13
typedef struct B B;

#pragma pack(push)  //将按1对齐的设置保存。
#pragma pack(2)     //设置新的对齐方式,结构体长度按2对齐
struct C{
    char A2;        //补0   偏0   长1
    long A1;        //补1   偏2   长10
    int A3;         //补0   偏10  长14    14是2的整数倍
};                  //总长14
typedef struct C C;

#pragma pack(4)     //设置新的对齐方式,结构体长度按4对齐
struct D{
    char A2;        //补0   偏0   长1
    long A1;        //补3   偏4   长12
    int A3;         //补0   偏12  长16    16是4整数倍
};                  //总长16
typedef struct D D;

#pragma pack(16)    //设置新的对齐方式,按16对齐。但实际成员最大长度为8,所以还是按8对齐。
struct E{
    char A2;        //补0   偏0   长1
    long A1;        //补7   偏8   长16
    int A3;         //补0   偏16  长20   20不是8整数倍,补4
};                  //总长24
typedef struct E E;

#pragma pack(pop)   //恢复之前保存的对齐方式,按1对齐
struct F{
    char A2;
    long A1;
    int A3;
};                  //总长13
typedef struct F F;


#pragma pack()      //恢复系统默认,按成员最大长度(8)对齐
struct G{
    char A2;
    long A1;
    int A3;
};                  //总长24
typedef struct G G;

设置对齐方式之后的内存计算
1、当设置的对齐长度小于当前成员长度时,成员偏移量是成员长度的整数倍。
2、当设置的对齐长度大于当前成员长度,并小于最长成员长度时,成员偏移量是设置的对齐长度的整数倍。
3、当设置的对齐长度大于最长成员长度时,成员成员偏移量按当前成员的实际大小对齐。
4、当设置的对齐长度小于实际最长成员长度时,结构体长度为设置的对齐长度的整数倍。
5、当设置的对齐长度大于或等于实际最长成员长度时,结构体长度为实际最长成员长度的整数倍。

为什么要对齐

现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。显然在读取效率上下降很多。


作者:Erice_e
来源:CSDN
原文:https://blog.csdn.net/erice_e/article/details/72851219
版权声明:本文为博主原创文章,转载请附上博文链接!
本文参考来源也是https://blog.csdn.net/erice_e/article/details/72851219,在此非常感谢。

你可能感兴趣的:(iOS - 结构体内存分配)