sizeof、pack和alignment

来源:互联网 发布:linux zip 安装包下载 编辑:程序博客网 时间:2024/05/19 08:05

本文简单总结了在考虑字节对齐的前提下,计算对象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)成员m,AR(m) = min(n, sizeof(m)),即AR(m)在m的size和对齐参数n之间取较小值;(标量类型指基本的字符、整数、浮点数、指针等。)

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

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

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

 

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

#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个字节上,从而占据4、5、6、7这四个字节。结果成员a之后有三个字节被“浪费”,沦为“packing bytes”;

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

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

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

 

然后考虑B:

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

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

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

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

 

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

20

108

 

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

#pragma pack(4)

换成

#pragma pack(8)

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

24

136

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

 

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

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/steedhorse/archive/2009/09/04/4520010.aspx

原创粉丝点击