您的位置:首页 > 其它

关于内存对齐

2009-06-03 17:34 337 查看
转自:http://blog.csdn.net/hikaliv/archive/2009/06/03/4239352.aspx

以下实验平台为 VC9。

这里先引出几条VC下结构体对齐的原则:

1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员自身大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)。

第一条与第三条针对结构体视为一个整体对像来说的,第二条是针对结构体成员来说的。

至于为何第一条与第三条都要去考虑“最长类型”,是因为如果声明为结构体的数组的话,那么对于该数组的每一个元素的成员的访问都必须“对齐”,这也就是为何必须要考虑“最长类型对齐”的原因。

这里还有一个问题,

#pragma pack(push)

#pragma pack(n)

.....

#pragma pack(pop)

这些可以改变对齐状态,同样,在编译命令行里面设置 /Zpn 也可以改变对齐的状态。

如果这里的 n 与结构体内的“最长类型”不一致怎么办?

取最小的对齐,若n较小,则“最长类型”失效,以n为准。

所以,上三条原则听是针对默认的情况来说的,default情况下n为多少呢?肯定大于sizeof(double)。

还有一个问题,如果结构体内部有数组,怎么算?比如说 short fuck[5]; 数组将被视为连续存在的基本类型,而不被当成一个整体,比如说 fuck 将被视为 5 个 short ,而不是一个 short[5]。

记得微软有本书上说,“聪明”的VC会调整成员的顺序,使其整个结构体的大小最小,比如说:

struct fuck
{
char cf;
int if;
short sf;
};

大小将是 12 ,但若将 cf 调至最末一个成员,则大小将缩为 8。

但我今天拿 VC9 试了一把,好像满不是那么一回事儿!看来还真是“百见不如一闻”啊……

所以说,程序员在自己写STRUCT的时候,一定要注意成员的顺序问题。

下面我查了GCC的原则:

在GCC中,对齐模数的准则是:对齐模数最大只能是 4,也就是说,即使结构体中有double类型,对齐模数还是4,所以对齐模数只能是1,2,4。而且在上述的三条中,第2条里,offset必须是成员大小的整数倍,如果这个成员大小小于等于4则按照上述准则进行,但是如果大于4了,则结构体每个成员相对于结构体首地址的偏移量(offset)只能按照是4的整数倍来进行判断是否添加填充。

我对GCC不了解,还请对此了解的行家指正。

还有,C++还有一个类继承的问题,就是父子类的内存对齐怎么办呢?

http://blog.csdn.net/pathuang68/archive/2009/04/24/4106016.aspx

玄机逸士前辈的这篇博文里有说明。直接看结论就好了。

前辈有两个结论,

其一,C++语言保证“出现在derived class中的base class suboject有其完整性”;

其二,derived class的对齐数 = min(指定的全局对齐数,max(base class的对齐数,derived class的对齐数))

第一个结论没有问题。

第二个结论,需要修正一下,考虑如下情况:

#define NA 2
#define NB 1
#pragma pack(push)
#pragma pack(NA)
class A
{
public:
int i;
char c;
};
#pragma pack(push)
#pragma pack(NB)
class B : public A
{
public:
int i;
char c;
};
#pragma pack(pop)
#pragma pack(pop)
void main()
{
printf ( "%d/n", sizeof(A));
printf ( "%d/n", sizeof(B));
system("pause");
}

输出是 6 和 11,若把NA和NB互换,则输出为 5 和 12。

这里面 6 和 5 是没问题的,关系在于派生类 B 的对齐怎么回事儿。

有人说 12 好理解,因为是 NB = 2 的整数倍,但问题在这儿,其实 B 在满足结论一的情况下的有效长度正是 6 + 5 = 11,它补了一位。

我的理解是,前辈的结论二可能是想证明父子类的对齐方式是相关的,而事实上,该例证明了,二者不相关,各按各的对齐方式来。

所以我将前辈的结论二拆成两个式子:

1.派生类的对齐数 = Sum (所有父类的对齐数) + 自身的对齐数;

2.类的对齐数 = Min (指定对齐数, 类成员最长字节数);

讨论完毕。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/hikaliv/archive/2009/06/03/4239352.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: