*** 作系统中的结构体对齐,字节对齐

 *** 作系统中的结构体对齐,字节对齐,第1张

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

2.性能原因:数据结构(尤其是栈)应该尽可能在自然边界上对齐,原因在于,为了访问未对齐的内存,处理器需要作两次内存访问,而对齐的内存访问仅需要一次访问。

规则:

数据成员对齐规则:结构(struct)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。

结构(struct)的整体对齐规则:自数据成员完成各自对齐后,结构本身也要对齐,对齐将按照#pragma pack指定的数值和结构最大数据成员长度中,比较小的那个进行

结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储

假设CPU要读取一个4字节大小的数据到寄存器中(假设内存读取粒度是4)

1.数据从0字节开始(内存对齐)

2.数据从1开始(内存不对齐)

当数据从0字节开始的时候,直接将0-3四个字节完全读取到寄存器,结算完成

当数据从1开始的时候,问题很复杂,首先将前4个字节读到寄存器,再次读取4-7字节的数据进寄存器,接着把0字节,567字节的数据剔除,最后合并1234字节的数据进寄存器,对一个内存未对齐的寄存器进行了这么多额外 *** 作,大大降低了CPU的性能。

这还属于乐观情况(性能原因),还有平台的移植原因,因为只有部分CPU肯干,其他部分CPU遇到未对齐边界就直接罢工了

字节对齐:

1.第一个成员在与结构体变量偏移量(offset)为0的地址处。

2.其他成员变量要对齐到对齐数的整数倍的地址处

    (1)对齐数=对齐系数与该成员大小的较小值。

    (2)如果有宏定义 #pragma pack(n)则它中的n 就是对齐系数。

    (3)VS中默认的对齐系数为8,linux中的默认为4.

3.结构体总大小为最大对齐数(每个成员变量除了第一个都有对齐数)的整数倍。

4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,

  结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

VC和GCC默认的都是4字节对齐,编程中可以使用#pragma pack(n)指定对齐模数。出现以上差异的原因在于,VC和GCC中对于double类型的对齐方式不同。

Win32平台下的微软VC编译器在默认情况下采用如下的对齐规则: 任何基本数据类型T的对齐模数就是T的大小,即sizeof(T)。比如对于double类型(8字节),就要求该类型数据的地址总是8的倍数,而char类型数据(1字节)则可以从任何一个地址开始。

Linux下的GCC奉行的是另外一套规则:任何2字节大小(包括单字节吗?)的数据类型(比如short)的对齐模数是2,而其它所有超过2字节的数据类型(比如long,double)都以4为对齐模数。

复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度。

struct{char a;double b;}

在VC中,因为结构中存在double和char,按照最长数据类型对齐,char只占1B,但是加上后面的double所占空间超过8B,所以char独占8B;而double占8B,一共16Byte。

在GCC中,double长度超过4字节,按照4字节对齐,原理同上,不过char占4字节,double占两个4字节,一共12Byte。

__attrubte__ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。#define __u8unsigned char#define __u16 unsigned short/* __attribute__ ((packed)) 的位置约束是放于声明的尾部“;”之前 */struct str_struct{__u8a __u8b __u8c __u16 d} __attribute__ ((packed))/* 当用到typedef时,要特别注意__attribute__ ((packed))放置的位置,相当于: * typedef struct str_stuct str * 而struct str_struct 就是上面的那个结构。 */typedef struct {__u8a __u8b __u8c __u16 d} __attribute__ ((packed)) str/* 在下面这个typedef结构中,__attribute__ ((packed))放在结构名str_temp之后,其作用是被忽略的,注意与结构str的区别。*/typedef struct {__u8a __u8b __u8c __u16 d}str_temp __attribute__ ((packed))typedef struct {__u8a __u8b __u8c __u16 d}str_nopackedint main(void){printf("sizeof str_struct = %d/n", sizeof(struct str_struct)) printf("sizeof str = %d/n", sizeof(str)) printf("sizeof str_temp = %d/n", sizeof(str_temp)) printf("sizeof str_nopacked = %d/n", sizeof(str_nopacked)) return 0}编译运行:[root@localhost root]# ./packedtest sizeof str_struct = 5 sizeof str = 5sizeof str_temp = 6sizeof str_nopacked = 6


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/9025192.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-24
下一篇 2023-04-24

发表评论

登录后才能评论

评论列表(0条)

保存