C++中的字节对齐 2021/4/02

C++中的字节对齐

每个内部类型自身也都有一个自己的对齐参数,一般来说这个对齐参数就是 sizeof(具体type) 的值,在 win32 平台上就是采用sizeof作为具体类型的自身对齐参数的,也就是讲,char 的自身对齐参数是 1 , short 是 2 , int 是 4, float 也是 4, double 是 8 等。

地址对齐是相对于结构的成员来说的,单个内部类型的变量这种就没什么对齐不对齐的说法了。

结构的成员按照结构中声明的顺序依次排放,对齐的意思是成员相对于结构变量的起始地址的相对对齐,关键是在于相对于结构变量的起始地址的偏移。

有效对齐参数,内部类型的有效对齐是指它的自身对齐参数和指定对齐参数中较小的那个对齐参数;结构类型的有效对齐参数是指它的成员中,有效对齐参数最大的那个值。数组的有效对齐就是它的成员类型的有效对齐。

地址的字节对齐规则

  1. (成员的起始地址相对于结构的起始地址的偏移) % (成员的有效对齐) == 0

  2. (结构的总大小) % (结构的有效对齐) == 0

  3. 如果无法满足对齐规则的话就填充字节直到满足对齐规则

**# pragma pack(16)**
struct A{
      char a;
      double b;
};


第一个成员的地址就是结构的起始地址,所以它的地址相对于结构的起始地址的偏移是 0,而 a 是 char 类型,它的自身对齐是 1 小于指定的对齐参数 16,所以 a 的有效对齐是1,a 的起始地址偏移也满足 0 % 1 = 0;第二个成员是 double 类型,其自身对齐参数是 8,也小于指定的对齐参数,所以它的有效对齐是 8,这样我们指定的 # pragma pack(16) 就相当于一点用都没有了。而 double 类型的成员 b 要想满足对齐规则就必须在 a 的后面填充字节以使得 b 的地址相对于结构的起始地址的偏移至少为 8。所以结构 A的内存布局会是这样:

00 CC CC CC CC CCCC CC 00 00 00 00 00 00 00 00

而 sizeof(A) = 16;0 分别表示 a 和 b 的位置,CC就是填充的 7 个字节。


**# pragma pack(16)**
struct A{
      char a;
      short c;
      double b;
};

同样指定的对齐参数仍然没任何作用,a 还是在偏移为 0 的地址上,c 在 a 之后,c 的有效对齐就是自身对齐 2,位于相对起始地址偏移为 2 的地址上,满足对齐要求 2 % 2 = 0,c 和 a 之间填充了 1 个字节。b 仍然位于偏移地址为 8 的地址上,b 和 c 之间填充了 4 个字节。结构 A 的内存布局如下:

00 CC 00 00 CC CCCC CC 00 00 00 00 00 00 00 00

而 sizeof(A) = 16;0 分别代表 a,c,b的位置,CC 就是填充字节。


**# pragma pack(16)**
struct A{
      char a;
      double b;
      short c;
};

指定对齐仍然没用,a 在偏移为 0 的地址上,b 在偏移为 8 的地址上,c 紧紧挨着 b 的屁股,因为此时的地址偏移 16 已经满足 c 的对齐要求 16 % 2 = 0;所以就没必要填充字节了。但是结构体 A 的总大小也要满足对齐规则的第二条,即 (结构的总大小)%(结构的有效对齐) == 0;而结构 A 的有效对齐就是各个成员中有效对齐最大的那个数,也就是 b的对齐参数 8,所以 A 的有效对齐就是 8,结构的总大小要满足对齐要求还必须在 c 后面填充 6 个字节。此时 A 的内存布局如下:

00 CC CC CC CC CCCC CC 00 00 00 00 00 00 00 00 11 11 CC CC CC CC CC CC

而 sizeof(A) = 24;0 分别代表 a,b 的位置,1111 代表 c 的位置。

**# pragma pack(4)**
struct A{
      char a;
      double b;
      short c;
};


把指定对齐参数设置成 4,此时 a 和 c 的有效对齐仍然是其自身对齐,而 b 因为它的自身对齐 8 大于了指定的对齐 4,所以 b 的有效对齐现在变成了 4 而不再是 8 了。a 仍然位于偏移 0,b 要满足对齐规则的话,地址偏移必须是其有效对齐的整数倍,所以 b 的偏移应该是 4,c 仍然紧紧跟在 b 的后面,因为此时的偏移 12 满足了 c 的对齐要求12 % 2 = 0;结构 A 的有效对齐现在也变成了 4,即等于成员中最大的有效对齐,b 的有效对齐。A 的总大小要满足对齐规则的话还必须在 c 的后面填充 2 个字节,让总大小变为 16 字节。此时 A 的内存布局如下:

00 CC CC CC 00 0000 00 00 00 00 00 11 11 CC CC

而 sizeof(A) = 16;0 分别代表 a,b 的位置,1111 代表c的位置。


**# pragma pack(8)**
struct A{
      char a;
      double b;
};
struct B{
int i;
   struct A sa;
int c;
};

我们来看结构 B 的布局,把指定对齐参数设置成 8,其实也还是没起作用,我们最大的内部类型就是 8 的 double 了,刚好和指定的对齐参数相等。第一个成员 i 肯定是位于偏移为 0 的地址上了。然后第二个成员是一个结构成员,我们要找到这个成员的有效对齐参数,结构的有效对齐参数是其成员中最大的那个对齐参数,对于结构 A 来说就是 b 的对齐参数 8,所以 A 的有效对齐是 8。结构成员 sa 要想满足对齐要求,即 偏移 % 有效对齐 8 = 0;它的地址偏移应该为 8。所以 sa 和 i 之间需要填充 4 个字节。成员 c 仍然紧紧跟在 sa 后面,因为 sa 占 16 字节,此时的地址偏移 24 已经可以满足 c 的对齐要求 24 % 4 = 0 ;而结构 B 的总大小也要满足对齐规则,B 的有效对齐就是成员中最大的,sa的有效对齐 8。所以 B 的总大小要能被 8 整除,就必须在 c 的后面再填充 4 个字节。此时结构 B 的内存布局如下:

00 00 00 00 CC CCCC CC 00 CC CC CC CC CC CC CC 00 00 00 00 00 00 00 00 11 11 11 11 CC CC CC CC

而 sizeof(A) = 32;0 分别代表 i,sa.a,sa.b 的位置,11111111代表 c 的位置。

**# pragma pack(8)**
struct A{
      char a;
      double b;
};
struct B{
int i;
int c;
   struct A sa;
};

把 c 移到 sa 的上面,这样就不需要填充任何字节了。B的所有的成员刚好满足对齐规则。注意,结构 A 中的 b 和 a 之间还是要填充字节的,它内部要满足自己的对齐要求。此时 B 的内存布局如下:

00 00 00 00 11 1111 11 00 CC CC CC CC CC CC CC 00 00 00 00 00 00 00 00

而 sizeof(A) = 24;0分别代表 i ,sa.a,sa.b的位置,11111111 代表 c 的位置。

结合位域结构体进行考查字节对齐

struct Test
{
	int a : 5;
	int b : 3;
	int c : 2;
	double d;
};

a,b,c都是在int里进行位的划分,所以只算一个int.

sizeof(Test) = 16;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值