美文网首页
结构体的内存对齐

结构体的内存对齐

作者: Bel李玉 | 来源:发表于2020-09-12 00:38 被阅读0次

在计算机系统中,为方便,快速的从内存中读取内存值,要求内存中的变量值需要按照一定的规则进行排放,在这篇文章中我们一起讨论结构体中的内存对齐。

内存对齐原则:

  • 1:数据成员对齐原则结构体(struct)联合体(union)的数据成员,第一个数据成员放在offset0的地方,以后每个数据成员存储的起始位置从该成员大小整数倍开始。
  • 2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小整数倍地址开始存储。
  • 3:收尾工作: 结构体的总大小,也就是sizeof的结果,必须是其内部最大成员整数倍,不足的要补齐。

分析:

单个结构体

我们通过探讨下面两个结构体的内存分布,来探讨该问题

//1、定义两个结构体
struct Mystruct1{
    char a;     //1字节
    double b;   //8字节
    int c;      //4字节
    short d;    //2字节
}Mystruct1;

![内存对齐2.png](https://img.haomeiwen.com/i1891462/460d8737b79e80e6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


//计算 结构体占用的内存大小
NSLog(@"%lu-%lu",sizeof(Mystruct1),sizeof(Mystruct2));

我们假设从内存为 0的位置开始存储

对于MyStruct1而言:

  • 1,char a: achar类型,占用 1字节位置 0存放 char a
  • 2,double b: bdouble类型,占用 8字节 ,根据 原则1,要从 8 的整数倍位置处存放b,则在 位置8 存放 double b的值。到目前为止,0~7存放的是 char a8 ~ 15存放的是double b
  • 3,int c : cint 类型,占用 4字节,根据 原则1,需要存放在 4的整数倍的位置,则 从 位置16开始存放。16~20存放 int c。此时 ,0~7存放的是 char a的值,8~15存放的是double b的值,16~19存放的是int c的值。
  • 4,short d: dshort类型,占用 2字节,应该从2的整数倍位置开始存储。则在 20~21 位置处存放 short d的值。

MyStruct1结构体的内存分布如下图所示,白色区域为填充区域,有颜色的地方为存放变量的区域。根据原则3结构体的大小为最大成员变量的整数倍原则,我们可以看出,其大小为 24.

内存对齐1.png

对于Mystruct2而言:

  • 1,double b为第一个成员,从 0位置 开始存储,占用 8字节,则 0~7 存放 double b的值。
  • 2,int c需要占用 4字节,需要从 4的整数倍开始存储,则 4~7存放 int c的值。
  • 3,short d需要占用 2字节,从2的整数倍位置开始存储,则 8~9 存放 short d的值。
  • 4,char a需要占用 1字节,从1 的整数倍位置开始存储,则 位置10 存放 char a的值。

下图为Mystruct2的内存分布,我们可以看出 Mystruct2的内存大小为 16

内存对齐2.png

⚠️⚠️⚠️我们通过上面两个例子,来讲解结构体的内存对齐原理,我们可以看出,当结构体的成员一样时,其占用的内存大小并不一致,其内存大小与成员的排列顺序有关系

结构体嵌套

为了分析结构体嵌套里面的内存分布,我们定义如下

struct Mystruct2{
    double b;   //8字节
    int c;      //4字节
    short d;    //2字节
    char a;     //1字节
}Mystruct2;

struct MyStruct3{
    char a;     //1字节
    double b;   //8字节
    struct Mystruct2 struc; // 16 字节
    int c;      //4字节
    short d;    //2字节

}MyStruct3;

MyStruct3中嵌套了 Mystruct2

  • 1,根据前面单个结构体里面阐述的char a 占用 0 ~ 7字节空间,double b 占用 8 ~ 15字节空间。
  • 2, 根据原则2Mystruct2结构体里面最大的成员变量8字节,所以,要从8的整数倍的位置处开始存放 Mystruct2168的整数倍,所以,16 ~ 31存放 Mystruct2
  • 3,int c,需占用4字节,需要从4的整数倍位置处存放,则 32 ~ 35处存放 int c
  • 4,short d,需要占用 2字节,从 2的整数倍位置处开始存放,则36 ~ 37存放 short d

根据以上分析,我们可以得到,MyStruct3占用了 0~37位置的空间,根据 原则3,它所占总空间应该为 其内部最大成员大小的整数倍MyStruct3的最大成员大小为 8,所以,其占用内存空间为 0 ~ 39,共 40字节空间。其内存分布如下图所示:

内存对齐3.png

内存优化(属性重排)

在我们讲解第一个示例的时候,我们可以看出,结构体内存的大小和成员变量的大小和其排放顺序有关,两个结构体如果拥有同样的属性,但其属性排放顺序不一致,会导致占用内存的大小不一致。原因如下:

  • 如果结构体内存成员变量是从小到大进行排布的,则需要更多的padding会导致占用的内存偏大。
  • 如果结构体内,成员变量是从大到小进行排布,则会有很少的padding,会使结构体占有更少的空间。
    在 iOS中,苹果会对进行属性重排,来达到优化内存的目的,我们以下面的代码为例
@interface LYPerson : NSObject

@property(nonatomic, copy) NSString *name;
@property(nonatomic, assign) char a;
@property(nonatomic, assign) int c;
@property(nonatomic, assign) short d;
@end

@implementation LYPerson

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LYPerson *person = [LYPerson alloc];
        person.c  = 5;
        person.a = 'a';
        person.name = @"LY";
        person.d = 2;
        NSLog(@"%ld", class_getInstanceSize(person.class));

    }
    return 0;
}

首先,我们来看下输出结果:

2020-09-12 13:24:17.099525+0800 KCObjc[59477:1943607] 24

为什么是24 ???

我们可以通过lldb指令查看一下内存分布情况

(lldb) po person                              \\  1
<LYPerson: 0x1012492c0>
(lldb) memory read/8gx 0x1012492c0 \\ 2
0x1012492c0: 0x001d80010000238d 0x0000000500020061
0x1012492d0: 0x0000000100001010 0x0000000000000000
0x1012492e0: 0x0000000000000000 0xbd2293c8794d4e12
0x1012492f0: 0x00000001003f4000 0x0000000000000001

(lldb) x/8gx 0x1012492c0            \\ 3
0x1012492c0: 0x001d80010000238d 0x0000000500020061
0x1012492d0: 0x0000000100001010 0x0000000000000000
0x1012492e0: 0x0000000000000000 0xbd2293c8794d4e12
0x1012492f0: 0x00000001003f4000 0x0000000000000001
  • 1,打印 person对象,得到 person对象的内存地址。
  • 2,读取0x1012492c0内存区域的存储值, memory read/8gx 地址指令用来读取内存值,
  • 3, x/8gx 0x1012492c0xmemory read/的缩写行式。8,是输出的数量个数,ggiant word 8字节,最后一个x表示以16进制格式输出
(lldb) po 0x001d80010000238d & 0x00007ffffffffff8ULL // 1
LYPerson 

(lldb) po 0x0000000100001010 // 2
LY

(lldb) po 0x0005 //3
5

(lldb) po 0x0002 // 4
2

(lldb) po 0x0061 //5 
97
  • 1,在该内存的前8个字节存储的是 对象的 isa值,0x00007ffffffffff8ULLISA_MASK
  • 2,从 16 ~ 23字节处,储存的是对象的name属性。
  • 3, 10 ~ 11字节处,储存的是 属性c的值。
  • 4,12 ~ 13 字节处,存储的是属性d 的值。
  • 5, 14 ~ 15字节处,存储的是属性a的值, 97字母aASCII值。

其内存分布如下所示:


LYPerson内存分布.png

总结:

我们在这里首先讨论了,内存对齐的三原则单个结构体的内存分布结构体嵌套的内存分布,以及 iOS中的内存对象的属性重排和其内存分布

相关文章

  • 结构体

    1.结构体 2.结构体的内存对齐模式 编译器在编译一个结构的时候采用内存对齐模式,结构体总是以最大的成员最为对齐单...

  • 内存对齐详解

    1、什么是内存对齐假设我们声明两个变量: 2、结构体内存对齐规则 结构体所占用的内存与其成员在结构体中的声明顺序有...

  • ndk-基础知识

    数组和指针,数组指针,指针数组 结构体 内存对齐 内存对齐 结构体大小 S5DUD10BN083MHEJEX7TP...

  • iOS原理探索02-- 内存对齐

    结构体内存对齐 我们首先定义两个结构体,分别计算他们的内存大小,并讨论内存对齐原理 从两个结构体来看两者没啥大的区...

  • iOS 内存对齐

    一、结构体内存对齐 1.1 结构体内存对齐三大原则 数据成员对⻬规则结构体(struct)或联合体(union)的...

  • 从结构体内存对齐到OC对象内存对齐

    1、结构体内存对齐 结构体对齐规则:1:数据成员对⻬规则:结构(struct)(或联合(union))的数据成员,...

  • 结构体

    结构体有名定义 无名定义 结构体嵌套定义 结构体内存对齐 结构体成员初始化 结构体变量引用 结构体的有名定义:直白...

  • iOS 内存对齐:结构体继承和结构体作为另一个结构体成员变量情况

    内存对齐规则就不赘述了,这里讨论下继承和结构体作为另一个结构体成员变量的情况下,结构体内如何进行内存对齐。以下测试...

  • 第一天,内存对齐

    一对齐规则: 1.非结构体类型 32位 4字节对齐,64位 8字节对齐 2.结构体类型 以结构体中,最大内存的整数...

  • iOS 结构体内存对齐

    结构体内存对齐规则 每个成员的对齐数为自身大小,每个成员的offset为自身对齐数的整数倍; 结构体整体的对齐数为...

网友评论

      本文标题:结构体的内存对齐

      本文链接:https://www.haomeiwen.com/subject/mpdiektx.html