数据对齐

来源:互联网 发布:有出售二手房软件 编辑:程序博客网 时间:2024/06/06 07:10

一、前述:数据对齐是可移植性很重要的一部分知识,学习这部分知识会让你知道为什么慎用memcmp来做整个c结构体的内容比较,注意数据对齐是编译器的操作。


二、什么是数据对齐?

对齐是跟数据块在内存中的位置相关的话题,对齐分为自然对齐和强制对齐;

自然对齐是指一个变量的内存地址正好是它长度的整数倍,也就是说,一个大小为2的n次方字节的数据类型,其地址的最低有效位的后n位都应该是0;

强制对齐是强制该变量进行对应字节的对齐。


三、为什么要进行数据对齐?

如果一个数据类型长度较小,用一个长度大的数据类型指针去进行类型转换,会出现对齐问题;


处理器在读取存储的数据时,有时候会二字节或者四字节的读,如果恰好你的数据是整齐的,那么会大大提高性能;举个例子:

struct A{

  unsigned short a;

  unsigned long b;

};

假设处理器一次性读4字节,处理器第一次读了a和一半的b,第二次读了一半的b,还需要对b进行高低位整合;如果此时进行4字节对齐,你会发现,处理器第一次读了a,第二次读了b,少了整合的过程。


四、结构体数据对齐规则

自然边界是指结构体中最大基本类型长度,默然对齐就是按自然边界进行数据对齐,举个例子:

struct A{

 char a;

 unsigned long b;

 unsigned short c;

 char d;

};

sizeof( A ) != 8,  sizeof( A ) = 12;

可以看到结构体A中最大基本类型长度为unsigned long 4字节, 按4字节进行对齐, 变量a后需要补3个字节, b不需要补, c + d = 3, 需要补1个字节,所以4 + 4 + 4 = 12;


#pragma pack(n) 用来指定按n字节进行对齐, #pragma pack() 用来取消默认对齐方式,举个例子

#pragma pack( 2 )

struct A{

 char a;

 unsigned long b;

 unsigned short c;

 char d;

};

#pragma pack()

sizeof( A ) = 10;

可以看到指定了按2字节进行对齐, 变量a后需要补1个字节,b不需要补, c不需要补, d需要补一个字节, 所以2 + 4 + 2 + 2 = 10;

注意,如果n大于结构体中最长基本类型长度,那么忽略n,仍然按最长基本类型长度进行数据对齐,即在上面例子中,#pragma pack(8)是没有用的;


__attribute__((aligned(m)))也是一种指定对齐的方法,但是它和#pragma有很大差别,首先如果m大于结构体中最长基本类型长度,则按m进行对齐,如果小于结构体中最长基本类型长度,那么就按最长基本类型长度进行数据对齐;

__attribute__((packed))是取消优化数据对齐,即进行单字节数据对齐,请注意__attribute__((aligned(1)))不等同于__attribute__((packed));

__attribute__((aligned(m)))还需要注意结构体中相邻的基本数据类型变量,举个例子:

struct A{

 char a;

 unsigned long b;

 unsigned short c;

 char b;

}__attribute__((aligned(8)));

struct B{

 unsigned long a;

 unsigned long b;

}__attribute__((aligned(8)));

struct C{

 unsigned short a;

 unsigned short b;

 unsigned long c;

}__sttribute__((aligned(8)));

sizeof( A ) = 16; sizeof( B ) = 8; sizeof( C ) = 8;

可以看到,其实我们定义的这三个结构体都是8字节长的变量,而且都是按8字节进行对齐,结果却不同;结论是编译器首先会把相同基本类型的变量遍历,直到不是相同的基本类型变量或者长度大于等于8字节长度,然后进行数据对齐。


有需要的交流的,可查看我的邮箱,谢谢