C/C++字节对齐

1. 字节对齐

字节对齐是字节按照一定规则在空间上排列。

现代计算机中内存空间的基础单元是字节(byte),从理论层面上讲,对于任何数据类型的变量的访问,都可以从任何地址开始。但是物理层面实现时,访问特定类型变量的时候经常需要在特定的内存地址访问,一般时以2,4或8的倍数的字节块来读写内存。

为什么要进行字节对齐?

一句话来说:以牺牲空间的方式来减少时间的消耗。

实际编程中,字节对齐的细节都是由编译器完成,但是需要知道变量是如何在内存中进行对齐的。

2. C/C++ 结构体

在C语言中,结构体可以用来存放一组不同类型的数据,结构体的定义形式为:

struct 结构体名
{
	结构变量...
};

结构体变量定义:

struct 结构体名 变量名;

也可以在定义结构体的同时定义结构体变量:

struct Student
{
	char name[24];
	int student_id;
}stu1, stu2; // 定义结构体变量stu1, stu2

当只需要stu1, stu2两个变量,后面不再需要使用结构体名定义其他变量,可以在定义时不给出结构体名

struct 
{
	char name[24];
	int student_id;
}stu1, stu2; // 定义结构体变量stu1, stu2

C++中的结构体定义形式与C语言一样,只是在定义结构体变量时,不需要使用struct关键字

Student stu1, stu2;

typedef struct的区别

在C语言中使用typedef定义结构名,在定义结构体变量时可以省去struct关键字

typedef struct Student
{
	char name[24];
	int student_id;
} pStudent;// 别名

// 定义结构体对象
pStudent stu1; // 省去了struct

使用typedef struct之后,C/C++定义结构体对象代码相同。

3. 结构体对齐的公式

  1. #pragma pack(x)x和结构体中占用空间最大的成员做比较,取两者的最小值为n
  2. n值与结构体每个成员比较,得出结果列表为m[x]
  3. 根据每个成员的大小依次向内存中填充数据,要求填充成员的起始地址减去结构体起始地址的差值可以整除m[x],如不能整除则向后移动,直到可以整除再填充成员到内存
  4. 当全部成员填充完毕后所占用的字节数若不能整除n,则扩充内存至可以整除n为止
#pragma pack(4)// 编译器将按照n个字节对齐
struct data
{
    int a;  // 4 bytes
    char b; // 4 bytes
    int c;  // 4 bytes
    short d;    // 4bytes
};  // 16 bytes
#pragma pack() // 编译器将取消自定义字节对齐方式

C/C++字节对齐_第1张图片

struct data
{
    int a;  // 4 bytes
    char b; // 2 bytes
    short d;    // 2bytes
    int c;  // 4 bytes
    
};  // 12 bytes

C/C++字节对齐_第2张图片

合理调整成员的位置,可以大大节省存储空间。但是需要在空间和可读性之间进行权衡。

4. 跨平台传输

由于不同平台对齐方式可能不同,可能导致同样的结构在不同的平台其大小可能不同,导致发送的数据可能出现错乱,引起严重问题。

为了不同处理器之间能够正确处理结构体,有两种可选的处理方法:

  • 1字节对齐
  • 自己对结构 进行字节填充

可以使用伪指令#pragma pack(n) 来使得结构之间按照一字节对齐

#pragma pack(1)
typedef struct
{
    int a;
    char b;
    short d;
    int c;
} DATA;
#pragma pack()

int main()
{
    DATA data1 = {4, 'c', 2, 6};
    DATA data2 = {};
    memcpy(&data2, &data1, sizeof(DATA));
    
    cout << "sizeof(DATA) > : " << sizeof(DATA) << endl;
    cout << "data: " << data2.a << " " << data2.b << " " << data2.c << " " << data2.d << endl;
    return 0;
}
// sizeof(DATA) > : 11
// data: 4 c 6 2

对于单个结构体,如下的方法,可使其按一字节对齐

typedef struct
{
    int a;
    char b;
    short d;
    int c;
} __attribute__ ((packed)) DATA;

__attribute__ ((packed)):取消结构在编译过程中的优化对齐,即可以认为是一字节对齐

人为填充方式

typedef struct
{
    int a;
    char b;
    char reserve[3];   // 填充
    int c;
    short d;
    char reserve1[2];  // 填充
} DATA;

参考链接

  • 字节对齐,看这篇就懂了
  • BMP图像读取
  • 结构体成员在内存中的对齐方式

你可能感兴趣的:(Linux,字节对齐,结构体,C/C++)