字节对齐看这篇就够了(内存对齐)

前言

大家好,我是小昭,因为在不同硬件平台数据传输时,遇到关于字节对齐的问题,索性就做了总结,以下是我对字节对齐的理解和小结,如有疑问请联系我。

目录

  • 结构体变量占用空间不同

  • 为什么要字节对齐?(内存对齐)

  • 字节对齐的规则

结构体变量占用空间不同
typedef struct data_type1{
    char a[2];
    short b;
    int c;
} Data_type1;
typedef struct data_type2{
    short a;
    int b;  
    char c[2];
} Data_type2;

先看看上面两个结构体,如果有定义成对应的变量,它俩占内存空间是多少 ?使用sizeof()占用多少空间,都是8个字节大小,答案不是。

Data_type1和Data_type2结构体里的成员变量类型相同,两者的所占的空间应该是相同的,但是事实却不是如此。

经过实践编写代码输出的结果,却是这样的。

/*ubuntu gcc环境 */
#include 
#include 
#include 

#define debug_printf(value) printf(#value " ---==> %d\n", value)
#define debug_struct_member_offset(struct, member) (((char *)(&(((struct *)0)->member))) - ((char *)0))
typedef struct data_type1{
    char a[2];
    short b;
    int c;
} Data_type1;
typedef struct data_type2{
    short a;
    int b;  
    char c[2];
} Data_type2;
int main(int argc, char** argv){
    int a,b,c;
    a = debug_struct_member_offset(Data_type1,a);
    b = debug_struct_member_offset(Data_type1,b);
    c = debug_struct_member_offset(Data_type1,c);
    debug_printf(sizeof(Data_type1));
    debug_printf(a);
    debug_printf(b);
    debug_printf(c);

    a = debug_struct_member_offset(Data_type2,a);
    b = debug_struct_member_offset(Data_type2,b);
    c = debug_struct_member_offset(Data_type2,c);
    debug_printf(sizeof(Data_type2));
    debug_printf(a);
    debug_printf(b);
    debug_printf(c);
}

字节对齐看这篇就够了(内存对齐)_第1张图片

Data_type1和Data_type2的类型变量占用的大小分别是8和12.

为什么会出现这样的结果?

因为编译器对数据进行了优化,做了字节对齐操作,可以增加内存的使用率和数据高效得传输。

什么是字节对齐?

现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但是实际的计算机系统对基本类型数据在内存中存放的位置有限制,计算机并非逐个字节读取,而是以2、4、8的倍数字节块读取内存,它们会要求这些数据的首地址的值是某个数是k(通常是4或8)的倍数 ,这就是所谓的内存对齐。大白话就是,各种数据类型都要一定的规则进行排列,而不是一个接一个的排放,这就是对齐。

为什么需要字节对齐?

1、减少cpu访问变量的次数,cpu可以更快的读取数据

2、合理的使用字节对齐,可以节省内存的大小。

3、减少 cpu 访问数据的出错性(有些 cpu 必须内存对齐,否则指针访问会出错),不同硬件平台进行数据通信,数据对齐可能会不一致,需要加入伪指令进行操作,防止灾难性性bug。

字节对齐的规则

规则一:结构体变量的首地址能够被字节对齐的大小整除(gcc 缺省字节对齐大小是4)。

规则二:结构体的每个成员相对首地址的偏移是成员类型大小的整数倍。(成员自身对齐)

规则三:结构体变量的总大小是结构体里最大的成员的整数倍。(结构体本身对齐)

计算结构体变量的字节大小,以上面的例子说明

Data_type1结构体变量类型,大概这样存放的。

在这里插入图片描述

计算字节大小可以不关心规则一,可以被忽略。看规则二,第二个成员b的偏移是2是short大小(2)的整数倍,第三个成员c的偏移量是4是int的大小的整数倍。看规则三,总的大小是8字节,是int大小的整数倍,c成员后面不需要填内容。

Data_type1结构体变量类型
在这里插入图片描述

看规则二,第二个成员b的偏移是2不是int大小(4)的整数倍,成员a后面补2个字节才能被4整除,第三个成员c的偏移量是8是char的大小的整数倍。看规则三,总的大小是10字节,不是是int大小(4)的整数倍,成员c补上内容(2),直到能被4整除,所以总大小是12个字节。

在设计数据结构的时候,可以看出合理调整成员的排序,可以节省内存使用。但是需要在空间上和可读性进行权衡。

思考,结构体类型,被实体化后,占用的字节大小:

typedef struct data_type3{
    char a;
    char b;  
    char c;
} Data_type3;
typedef struct data_type4{
    char a;
    char b[2];  
    //char c;
} Data_type4;

答案都是3.

有什么办法可以对字节对齐进行干预?

1、使用#prama park(n)伪指令,会对前面规则二和规则三进行干预,大白话,成员本身字节对齐和结构体本身对齐大小不能大于n,只能小,如果能理解这这句话,问题就不大了,直接看例子吧。

#include 
#include 
#include 

#define debug_printf(value) printf(#value " ---==> %d\n", value)
#define struct_member_offset(struct, member) (((char *)(&(((struct *)0)->member))) - ((char *)0))

#pragma pack(4)  //4字节对齐开始 对齐大小只能比4小,不能大
typedef struct
{
  uint32_t b;    
  /*double 8字节对齐,这里正常是要补4字节内容,
  但是编译器这里规定最多只能4个字节对齐,
  如果是short就按2个字节对齐,只能小不拿大*/
  double   f;
} test_t;
#pragma pack()//字节对齐结束
int main()
{
  int b = struct_member_offset(test_t, b);
  int f = struct_member_offset(test_t, f);

  debug_printf(sizeof(test_t));
  debug_printf(b);
  debug_printf(f);
  return 0;
}

运行结果:
字节对齐看这篇就够了(内存对齐)_第2张图片

注释#pragma pack(4) 占用空间是16.

字节对齐看这篇就够了(内存对齐)_第3张图片

2、使用__attribute((aligned(n))),操作结构体时,这里可以看成是对规则三进行干预,对结构体本身对齐。

typedef struct
{
  uint8_t  a;    // 偏移地址
  uint16_t b;    // 偏移地址
}__attribute((packed)) test_t1; //按实际空间 
typedef struct
{
  uint8_t  a;    // 偏移地址
  uint16_t b;    // 前补1 偏移地址
}__attribute((aligned(4))) test_t2;
typedef struct
{
  uint8_t  a;    // 偏移地址
  uint16_t b;    //前补1 偏移地址
}__attribute((aligned(8))) test_t3;
typedef struct
{
  uint8_t  a;    // 偏移地址
  uint16_t b;    //前补1 偏移地址
}__attribute((aligned(16))) test_t4;
//#pragma pack()
int main()
{
  debug_printf(sizeof(test_t1));
  debug_printf(sizeof(test_t2));
  debug_printf(sizeof(test_t3));
  debug_printf(sizeof(test_t4));
  return 0;
}

运行结果

字节对齐看这篇就够了(内存对齐)_第4张图片

留个坑:


typedef struct data_type1{
    char a[2];
    short b;
    int c;
} Data_type1;
typedef struct data_type2{
    short a;
    Data_type1 b; //Tips: 这部分按里面最大成员大小对齐 所以要补. 至于补多少……
    char c[2];
} Data_type2;

sizeof(Data_type2)是多少?

答案:16

以上是结果,都是经过我测试验证成功,如有疑问,请麻烦联系我。我是小昭!

你可能感兴趣的:(C++,C语言,c语言,linux)