说了N多年都说不清的结构体对齐问题

来源:互联网 发布:popsub字幕制作软件 编辑:程序博客网 时间:2024/06/07 01:58

作者:小马

 

一 引言
先要明确一个事情,结构体的对齐规则到底和什么有关系?. 经过本人”深入的研究和探索”, 答案是, 和编译器,操作系统, CPU都有关系.

和编译器有关系相信很多人都不怀疑, 和cpu和操作系统有还关系估计很多人也知道,问题是网上很少看到有人探讨这个, 这个问题并不是简单的总线长度就可以解释的清楚的,而且不同的CPU处理机制有很大的不同, 要把每个CPU的机制都讲清楚也不是容易的事情. 如果兴趣,可以搜一下国外的网站,估计有人会讲得很明白.

在网上经常可以看类似下面这样的笔试题目:
Struct A
{
 ….
 …
} A
问题sizeof(A)是多少?
按照上面的说法, 结构体的对齐规则和环境有关系, 那么这个题是不是有问题呢? 出题者是不是要给出在什么环境下呢? 我的答案是, 不一定.

因为我觉得至少存在以下两种可能性:
1 出这个题的公司本身对结构体对齐的问题就不是很了解,随便在网上找个题,或者在自己的电脑上随便找个编译器, 把算出的结果作为标准答案. 他们对这个题的标准答案就是简单一个数字(一般是12, 24或16), 仅此而已,没有任何深入的探讨.

2 有些公司确实有牛人在, 他们对这个问题理解的很清楚,但是,题目依然是像上面那样,没有给出在什么环境下运行。 他们就是想看一下应聘者对这个问题的认识程度, 如果应聘者的答案仅是一个数字, 很遗憾, 这一题没过关(即便可能在大多数环境下都是这个值). 比较好的答案是, 说清楚在什么环境下的结果是多少,最好还能写出你的结果是如何算出来的.

如果你面试的时候,碰到上面1情况的公司,劝你还是不要进了,估计做技术不咋的.

So…., 还有一个结论, 网上很多关于结构体内存对齐的文章, 只要是没说明环境的,统统都是错误的.

二 规则
先说说自己的环境,
x86+Xp+visual studio 2005
首先,你要了解在上面这个环境下, 一些基本的数据类型在内存中所占的字节长度, 这个长度也是它们的对齐模数,  如下:

编译器本身有一个默认的对齐模数, 在visual 2005下这个值是8(微软的编译器好像都是这个值), 为了方便描述,我把个值叫做”编译器对齐模数”, 这个东西有什么用,先不说,先看看如何修改它. 如果你不爽这个默认的对齐模数,可以修改它,方法是用下面这样的语句:
#pragma pack(n)
n的值就是你想要的对齐模数, 但不是你想指定什么值都可以, 有两点要注意:
1 比默认值大的pack将被忽略.比如编译器默认的对齐模数是8, 你在程序里加上下面一个语句:
#pragma pack(20)
这个是没有意义的,编译器认为对齐的模数还是8, 比8小的n值才是可以接受的.
2 有效的值只有1, 2, 4, 8, 16,其它值都是无效的

重点要来了, 结构体对齐的规则到底是怎样的呢? 其实就两条:
1 结构体中的成员按照某个模数对齐, 这个模数是”编译器对齐模数”和上述表格中自己模数中的较小者

2 按照1的规则算出来的字节数如果不是”编译器对齐模数”的倍数,就要补字节,直到是该模数的倍数.

 

三 例子
举个例子, 生动描述一下
#pragma pack(2)
struct S {
 int i;   // size 4
 short j;   // size 2
 double k;   // size 8
};
“编译器对齐模数”是2,
Int i点据0~3的位置, short j占据4~5的位置, double占据6~13的位置, 一共用了14个字节, 并且是” 编译器对齐模数”的倍数, 所以sizeof(S) 的结果是14.

如果注释掉 #pragma pack(2)这一行, 计算过程如下:
“编译器对齐模数”是8,
Int i点据0~3的位置, short j占据4~5的位置, 然后6~7补两个字节, double占据8~15的位置, 一共用了16个字节, 并且是” 编译器对齐模数”的倍数, 所以sizeof(S) 的结果是16.

 

下面再给几个例子, 不给出计算过程,只有答案, 有兴趣的可以自己算一下

#pragma pack(2)
struct S {
 char a;
 double k;   // size 8
};
结果是10, 如果没有#pragma pack(2), 结果是16.

#pragma pack(2)
struct S {
 char a;
 double k;   // size 8
 char c;
};
结果是12, 如果没有#pragma pack(2),结果是12

#pragma pack(2)
struct S {
 char a[11];
 int k;   // size 8
 char c;
};
结果是18, 如果没有#pragma pack(2),结果是20

如果是嵌套结构体的情况,即结构体还有结构体,把里面的结构体展开就可以了.

(仅一家之言, 欢迎拍砖)

原文 http://ponymaggie.blog.sohu.com/163447240.html

原创粉丝点击