[C/C++]结构体大小的计算方法

来源:互联网 发布:js颜色代码 编辑:程序博客网 时间:2024/05/18 00:35

通常情况下,由于地址对齐(alignment)的需要,结构体各成员之间或结构体的尾部需要添补一些空白字节(padding),因而结构体的大小并不一定等于各成员大小之和。包含相同成员的结构体,如果成员声明的顺序不同,其占用的内存空间也可能不同。尽管在程序中可以利用sizeof获取当前编译条件下结构体所占内存空间的大小,但深入了解结构体中成员对齐所遵循的规则也很有必要。

首先需要说明一下对齐的概念。结构体成员的对齐是指成员相对于结构体首地址的偏移量(offset)应该被2^n整除,n通常取0、1、2、3、4,称此时的对齐方式为2^n-byte对齐。例如2-byte对齐的时候,成员的偏移量只能是0、2、4……而4-byte对齐的时候偏移量只能是0、4、8……在C语言中,查看成员的偏移量可以利用stddef.h中定义的宏offsetof(T, member),offsetof的定义如下:

#define offsetof(T, member) ((size_t)&((T *)0)->member)

结构体中各成员以什么方式对齐与成员的大小有关,成员大小为a则进行a-byte对齐,即成员的偏移量应该被成员的大小整除。例如short类型的成员其偏移量必须是2的整数倍,double类型的成员其偏移量必须是8的整数倍。一个简单的例子如下所示:

struct sExample1
{
    short member1; //偏移量为0,能够被2整除
    //member1后添加6个字节的padding
    double member2; //偏移量必须为8才能被8整除
};

sExample1中member1为short类型,大小为2,采用2-byte对齐的方式,偏移量为0即可。member2为double类型,大小为8,需满足8-byte对齐的条件,因此其偏移量只能为8。在member1和member2之间添加6个字节的padding,因此sExample1的大小为16。

除了在成员之间添加padding以外,在结构体的尾部也可能添加padding,以使得结构体的大小能够被最大成员的大小整除。例如:

struct sExample2
{
    double member1;
    short member2;
    //member2后添加6个字节的padding
};

sExample2中,虽然member1和member2之间没有加入padding,但由于其最大成员(member1)的大小为8个字节,因此在member2之后添加了6个字节的padding,以使得结构体的大小能够被8整除。sExample2的大小仍然为16。

在VC的编译器中,通过#pragma pack可以限制添加padding的字节数。例如在定义结构体之前添加#pragma pack(n),则编译器为某个成员添加的padding的字节数不能超过n减去该成员的大小,这里n的取值可以是1、2、4、8、16。例如:

#pragma pack(4)
struct sExample3
{
    short member1;
    //member1后添加2个字节的padding
    double member2;
};
#pragme pack()

由于添加了#pragma pack(4),member1后添加padding的字节数不能超过4-sizeof(short),即2字节。例中#pragma pack()将padding的字节限制设置成默认值。

MSDN提供了一些计算结构体大小的典型例子,例如:

struct sExample4
{
    short member; //offsetof(sExample4, member) == 0
}; //sizeof(sExample4) == 2

struct sExample5
{
    int member1; //offsetof(sExample5, member1) == 0
    double member2; //offsetof(sExample5, member2) == 8
    short member3; //offsetof(sExample5, member3) == 16
}; //sizeof(sExample5) == 24

struct sExample6
{
    char member1; //offsetof(sExample6, member1) == 0
    short member2; //offsetof(sExample6, member2) == 2
    char member3; //offsetof(sExample6, member3) == 4
    int member4; //offsetof(sExample6, member4) == 8
}; //sizeof(sExample6) == 12

#pragma pack(4)
struct sExample7
{
    int member1; //offsetof(sExample7, member1) == 0
    double member2; //offsetof(sExample7, member2) == 4
    short member3; //offsetof(sExample7, member3) == 12
}; //sizeof(sExample5) == 16
#pragma pack()

#pragma pack(1)
struct sExample8
{
    char member1; //offsetof(sExample8, member1) == 0
    short member2; //offsetof(sExample8, member2) == 1
    char member3; //offsetof(sExample8, member3) == 3
    int member4; //offsetof(sExample8, member4) == 4
}; //sizeof(sExample6) == 8
#pragma pack()

References

  1. http://msdn.microsoft.com/en-us/library/aa290049(VS.71).aspx
  2. http://msdn.microsoft.com/en-us/library/2e70t5y1.aspx
  3. http://msdn.microsoft.com/en-us/library/71kf49f1.aspx