sizeof、pack和alignment

本文简单总结了在考虑字节对齐的前提下,计算对象size的基本规则。但本文只说结论,不讨论初衷和更深奥的原理。有兴趣的朋友可以读读这个:http://msdn.microsoft.com/en-us/library/aa290049(VS.71).aspx

 

为方便叙述,我们先定义一个概念:“对齐要求”(Alignment Requirement),并用AR来表示,即编译器将把一个对象m对齐到AR(m)的整数倍的地址上,比如32位的int变量将被安放到某个从4的整数倍开始的内存地址上。

编译器在计算一个用户自定义类型(比如结构体、类、联合体)的size时,不仅要考虑它每个成员的size,还要考虑字节对齐的要求。基本的规则是这样的:

假如预处理指令(如VC中的#pragma pack(n))或者编译器选项(比如cl/Zp选项)指定的pack值为n:那么

(1)         对于结构体中的标量类型(scalar type)成员mAR(m) = min(n, sizeof(m)),即AR(m)msize和对齐参数n之间取较小值;(标量类型指基本的字符、整数、浮点数、指针等。)

(2)         数组成员的AR跟数组中每个元素的AR相同;

(3)         整个结构体的AR取结构体中各个成员的AR的最大值;

(4)         应用以上所有规则之后,整个结构体的size还要进一步填充到结构体AR的整数倍上。

 

举个例子:(在32VC中考虑)

#include <iostream>

using namespace std ;

 

#pragma pack(4)

 

struct A {

  char a;

  int b;

  short c;

  double d;

};

 

struct B {

  short e;

  A f[5];

  char g;

};

 

int main() {

  cout << sizeof(A) << endl;

  cout << sizeof(B) << endl;

  return 0 ;

}

首先考虑A

(1)         由于AR(a) = min(4, sizeof(char)) = 1,不需要对齐,而且它是第一个成员,本来就该放在结构体的第0个字节上;

(2)         AR(b) = min(4, sizeof(int)) = 4,因此它要被对齐到第4个字节上,从而占据4567这四个字节。结果成员a之后有三个字节被“浪费”,沦为“packing bytes”;

(3)         后面AR(c) = min(4, sizeof(short)) = 2,因此它紧跟在i之后,从第8个字节开始并占据89两个字节,之前不需要再有填充;

(4)         后面AR(d) = min(4, sizeof(double)) = 4,因此它要被对齐到第12个字节上,并占据12~198个字节;而之前又有两个字节沦为“packing bytes”了;

(5)         从以上来看,四个成员所用过的最大AR4,即AR(A) max(AR(a), AR(b), AR(c), AR(d)) = 4。而四个成员断断续续地占用了0~1920个字节,而20正好也是4的整数倍,因此sizeof(A) = 20

 

然后考虑B

(1)         AR(e) = min(4, sizeof(short)) = 2,它被放在B的第0个字节上并占据01两个字节;

(2)         AR(f) = AR(f[0]) = AR(A),前面已经计算过,AR(A) = 4,所以f要从字节4开始,即e之后被packing了两个字节;然后,由于sizeof(f) = 5 * sizeof(A) = 5 * 20 = 100,所以它将占据4~103100个字节;

(3)         之后AR(g) = min(4, sizeof(char)) = 1,它不需要对齐,直接放在第104个字节上并占据这一个字节。

(4)         AR(B) max(AR(e), AR(f), AR(g)) = 4。而三个成员占据了0~104105个字节,但105并非4的整数倍,于是末尾需要再填充几个字节使它成为4的整数倍,即:sizeof(B) = 108

 

综上所述,之前那个程序将输出:

20

108

 

感兴趣的朋友可以试着把开头的

#pragma pack(4)

换成

#pragma pack(8)

并重新推导一下,程序的输出将变成:

24

136

注意,pack(8)32VC的默认设置。另外,VC中另一个影响自定义类型的alignmentsize的“__declspec(align(n))”,本文就不予讨论了。

 

顺便提一下:这些规则有兴趣的话了解一下就OK,没兴趣的话完全可以不去了解。实际的工程代码中除非必需,否则不要写出对字节对齐有依赖的代码。尤其在网络通信程序中,大多数时候都没必要。不要以为把一个结构体pack(1)一把然后直接send出去或recv进来效率就很高,网络收发的效率更大程度上取决于下层的缓冲算法,而非send/recv被调用的次数和方式。为了使程序更加可移值,还是采用在发送端构造发送缓冲,或者逐成员发送、而接收端逐成员解析的方法比较好。有些处理器不支持pack(1)的对象布局,或者支持地不好,于是轻者降低内存访问效率,重者造成错误或异常。

 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值