结构体&联合体&位域

433 阅读5分钟

1、各类型所占字节长度

signed char.png

2、结构体

结构体是由一批数据组合而成的一种新的数据类型。组成结构型数据的每个数据称为结构型数据的“成员”(引用于百度百科)。所占内存长度是各成员占的内存长度的总和。

2.1、结构体内存对齐原则

  • 1:第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储。
  • 2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)。
  • 3:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的整数倍.不足的要补⻬。

举个栗子:

struct JLObject {
    double a;            [0-7]    //8字节,从0开始,   放到[0-7]。
    char b;	         [8]      //1字节,8可以整除1,放到[8]。
    int c;	         [12-15]  //4字节,9不能整除4,找到距离最近可整除4的数为12,放到[12-15].
    short d;	         [16-17]  //2字节,16可整除2, 放到[16-17]
}object;                 总大小:24//实际占用18个字节,总大小应该是最大成员的整数倍,所以此处为24

//结构体嵌套的情况
struct JLObject1 {
    double a;	         [0-7]    //8字节,起始位置为0,放到[0-7]
    int b;        	 [8-11]   //4字节,8可以整除4, 放到[8-11]
    char c;		 [12]     //1字节,12可以整除1,放到[12]
    short d;		 [14-15]  //2字节,13不能整除2,下一个整倍数14,所以放到[14-15]
    int e;		 [16-19]  //4字节,16可以整除4,所以放到[16-19]
    struct JLObject obj; [24-47] //最大元素double8字节,所以要从下一个8的倍数24开始,JLObject总大小是24,所以 结果是[24-47]
}object1; 		 总大小:48

打印验证一下:

截屏2021-06-09 下午2.29.55.png

2.2、对齐原因

提高访问速度:

  • 未对齐的内存,处理器需要做多次内存访问,而对齐的内存仅需要一次。 如图,因为内存未进行对齐,所以cpu不能准确定位到内存边界,可能就会出现下面的情况,先读取一次内存,将2、3、4数据放进寄存器,再读取一次内存,将5放进寄存器,还需要把多余的数据剔除,这会大大降低访问效率。

截屏2021-06-10 下午3.07.48.png

平台移植:

  • 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2.3、内存优化

不同的排列占用内存大小也不同,系统对对象也有相应的优化,后面具体分析。

截屏2021-06-09 下午7.22.11.png

重排后内存占用由24缩减到16。 截屏2021-06-09 下午7.24.46.png

  • 由于内存对齐的影响,完全相同的类型组合,会因为排列不同,而影响到最终的内存占用。

3、联合体

在进行某些算法的C语言编程的时候,需要使几种不同类型的变量存放到同一段内存单元中。也就是使用覆盖技术,几个变量互相覆盖。这种几个不同的变量共同占用一段内存的结构,在C语言中,被称作“共用体”类型结构,简称共用体,也叫联合体。

“联合”与“结构”有一些相似之处。但两者有本质上的不同。在结构中各成员有各自的内存空间,一个结构体变量的总长度大于等于各成员长度之和。而在“联合”中,各成员共享一段内存空间,一个联合变量的长度等于各成员中最长的长度。应该说明的是,这里所谓的共享不是指把多个成员同时装入一个联合变量内,而是指该联合变量可被赋予任一成员值,但每次只能赋一种值,赋入新值则冲去旧值。(百度百科)。 比如isa指针、runloop的source 都是联合体的实际应用。

3.1、联合体内存对齐原则:

  • 1、联合体的所有成员相对于基地在的偏移量都为0;
  • 2、宽度要大到足够容纳最宽的成员(一般为其最宽成员所占的大小);
  • 3、大小能被其包含的所有基本数据类型的大小所整除;

例子:

union Un
{
    char a[10];//10字节
    int b;//4字节
    short c;//2字节
    double d;//8字节
};         
总大小:16字节。
分析:所有成员共用一片内存控件,偏移量都为0;
最宽成员char a[10]占用10字节,其大小应该是所有基本数据类型成员宽度的最小公倍数,也就是16

4、位域

在结构体或者联合体中,以位为单位来指定其成员所占内存长度,把数据以位的形式紧凑的储存,并允许程序员对此结构的位进行操作。

通常我们操作结构体、联合体的时候都是以字节为单位。如果用结构体有四个布尔类型的成员,则需要占用4个字节的内存空间,也就是32位。

struct JLObject {
    bool a;
    bool b;
    bool c;
    bool d;
}object;

如果使用结构体位域来存储,则只会占用一个字节的空间,假如四个值都为yes,在一个字节上将存储成00001111,nonpointer指针就是结构体位域的一个例子。

struct JLObject {
    BOOL a: 1;//a:成员名,1:该成员占用几位的空间
    BOOL b: 1;
    BOOL c: 1;
    BOOL d: 1;
};