5分钟搞定内存字节对齐

http://blog.csdn.net/hairetz/archive/2009/04/16/4084088.aspx

 

5分钟搞定内存字节对齐 收藏
写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢?讲讲字节对齐吧.

/******************************分割线

如果体系结构是不对齐的,A中的成员将会一个挨一个存储,从而sizeof(a)为11。显然对齐更浪费了空间。那么为什么要使用对齐呢?
体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。比如说读写时.............此处省略50万字

***********************************************************/

上面是你随便 google一下,人家就可以跟你解释的,一大堆的道理,我们没怎么多时间,讨论为何要对齐.直入主题,怎么判断内存对齐规则,sizeof的结果怎么来的,请牢记以下3条原则:(在没有#pragma pack宏的情况下,务必看完最后一行)

1:数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储。

2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)

3:收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的整数倍.不足的要补齐.

等你看完此3条原则,2分钟已经过去,抓紧时间,实战3分钟:

typedef struct bb
{
 int id;             //[0]....[3]
 double weight;      //[8].....[15]      原则1
 float height;      //[16]..[19],总长要为8的整数倍,补齐[20]...[23]     原则3
}BB;

typedef struct aa
{
 char name[2];     //[0],[1]
 int  id;         //[4]...[7]          原则1

 double score;     //[8]....[15]    
 short grade;    //[16],[17]        
 BB b;             //[24]......[47]          原则2
}AA;

int main()
{
  AA a;
  cout<<sizeof(a)<<" "<<sizeof(BB)<<endl;
  return 0;
}

结果是

48 24
ok,上面的全看明白了,内存对齐基本过关.

再讲讲#pragma pack().

在代码前加一句#pragma pack(1),你会很高兴的发现,上面的代码输出为

32 16
bb是4+8+4=16,aa是2+4+8+2+16=32;

这不是理想中的没有内存对齐的世界吗.没错,#pragma pack(1),告诉编译器,所有的对齐都按照1的整数倍对齐,换句话说就是没有对齐规则.

明白了不?

那#pragma pack(2)的结果又是多少呢?对不起,5分钟到了,自己去测试吧.

ps:Vc,Vs等编译器默认是#pragma pack(8),所以测试我们的规则会正常;注意gcc默认是#pragma pack(4),并且gcc只支持1,2,4对齐。套用三原则里计算的对齐值是不能大于#pragma pack指定的n值。

发表于 @ 2009年04月16日 14:15:00 | 评论( 46 ) | 编辑| 举报| 收藏

旧一篇:setsockopt 设置socket | 新一篇:使用TCP协议的NAT穿透技术 (转)
查看最新精华文章 请访问博客首页相关文章 Jalien 发表于2009年4月30日 22:01:52  IP:举报回复删除
这个太有用了 !上次就是遇到这个问题 ,被搞糊涂了 网上好多东西都没说清楚 谢了~qq675927952 发表于2009年5月5日 16:12:57  IP:举报回复删除
upedward9092 发表于2009年6月10日 23:44:44  IP:举报回复删除
很有用。。顶一下 Vegertar 发表于2009年7月17日 13:52:44  IP:举报回复删除
不错,收藏一下。jn989 发表于2009年7月17日 14:00:20  IP:举报回复删除
内存对齐的规则已经知道了,想问一下,其中第二条和第三条规则的原因是什么呢?(上面说为什么内存对齐好像只是规则1的解释)hairetz 发表于2009年7月18日 12:44:54  IP:举报回复删除
原因我就没有具体研究了。网上有几篇写的很清楚的。你把我的分割线里的内容放进google里就可以找到很多文章了。ms0614 发表于2009年7月21日 14:59:06  IP:举报回复删除
typedef union { long i; int k[5]; char c;} DATE; //大小为20 struct data { int cat; DATE cow; double dog; } too; int main() { cout << sizeof(data) << " " << sizeof(DATE); } 问题1:如果按照第三条原则, sizeof(data)大小应该为20的整数倍,但实际却是32,为什么? typedef struct bb { int id; //[0]....[3] double weight; //[7].....[15]      原则1 问题2:为什么不是[4]……[11]根据原则1,4就是4的倍数。 float height; //[16]..[19],总长要为8的整数倍,补齐[20]...[23]     原则3 }BB; abcdef0966 发表于2009年7月22日 13:46:30  IP:举报回复删除
有问题的hairetz 发表于2009年7月23日 15:37:40  IP:举报回复删除
有什么问题就说出来,一句有问题的,是什么意思? rejoice914 发表于2009年7月26日 17:34:05  IP:举报回复删除
typedef struct bb { int id; //[0]....[3] double weight; //[7].....[15]      原则1 这里应该是[7]...[14] 吧,double 8字节啊! float height; //[16]..[19],总长要为8的整数倍,补齐[20]...[23]     原则3这里应该是[15]....[18],总长要为8的整数倍,后面补齐[19]....[23] }BB;hairetz 发表于2009年8月2日 22:09:47  IP:举报回复删除
谢谢楼上,其实是[8]..[15] 打错了cir2633 发表于2009年8月17日 7:51:08  IP:举报回复删除
其实对齐是取决于cpu和寄存器的 gcc的对齐和vc的对齐策略不一样 LZ说的是vc对齐在gcc上编译运行的话,结果和vc上是不一样的 gcc默认全部成员与4对齐lyywo333 发表于2010年1月4日 23:53:07  IP:举报回复删除
flyyyri 发表于2010年1月6日 0:29:45  IP:举报回复删除
好,收藏了shuxieweilai 发表于2010年1月17日 13:58:54  IP:举报回复删除
太感谢了 以前老是遇到这样的问题一直没弄明白 收益非浅啊cocat 发表于2010年1月19日 14:26:17  IP:举报回复删除
相当有用,学习了reticentme 发表于2010年1月19日 22:51:15  IP:举报回复删除
太帅了,老师kongzhongxx 发表于2010年3月31日 10:45:19  IP:举报回复删除
ding,楼主太帅了jdc317264476 发表于2010年4月1日 13:45:08  IP:举报回复删除
cx369421368 发表于2010年4月1日 16:58:01  IP:举报回复删除
顶。dadun 发表于2010年5月13日 10:15:34  IP:举报回复删除
看了,不知道为啥BB的第二个数据不从[4]开始啊?4也是int的整数倍啊...为啥中间要空余出4来呢?dadun 发表于2010年5月13日 10:36:46  IP:举报回复删除
是不是double的开始储存位置也要是本身的倍数,所以要是8的倍数呢?hairetz 发表于2010年5月13日 11:50:30  IP:举报回复删除
回复 dadun:是的。1:数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储。
benjamin123go 发表于2010年6月11日 13:02:00  IP:举报回复删除
benjamin123go 发表于2010年6月11日 13:05:21  IP:举报回复删除
xty_seven 发表于2010年6月11日 15:27:54  IP:举报回复删除
 谢谢楼主啦! 简单易懂 没有废话 高兴啦 支持匿名用户 发表于2010年7月7日 17:57:13  IP:举报回复删除
zou_cplus 发表于2010年7月20日 11:16:53  IP:举报回复删除
是的。1:数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储。 那要是第一个数据成员是long的话,以后每个数据的起始位置都要是8的倍数?那我 typedef struct bb { double weight; int id; float height; char A; }BB; 的大小为什么还是24,按你的规则应该是32吧hairetz 发表于2010年7月20日 15:11:43  IP:举报回复删除
回复 zou_cplus: typedef struct bb { double weight; 0-7 int id; 8-11 float height; 12-16 char A; 17 }BB; 补齐到24字节。你的32是怎么算出来的?
zou_cplus 发表于2010年7月20日 15:44:27  IP:举报回复删除
回复 hairetz:1:数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始 这句话的意思 是不是说 后面的成员的起始位置 都是以第一个 成员大小的整数倍 位置开始啊
hairetz 发表于2010年7月20日 16:16:23  IP:举报回复删除
回复 zou_cplus:该成员,不是指第一个成员。
zou_cplus 发表于2010年7月20日 16:26:06  IP:举报回复删除
回复 hairetz:知道了 刚刚理解有点错误了
maoxing63570 发表于2010年8月30日 11:47:00  IP:218.63.63.*举报回复删除
#include <iostream> using namespace std; typedef struct Student1 { char name[20];//20个字节[0]....[19] char sex;//1个字节[20]...[23] long num;//4个字节[24]...[27] float score[3];//12个字节[36].......[47]原则3,则后面还要有[48]...[60] }; void main() { Student1 a; cout<<sizeof(Student1)<<" "<<sizeof(a)<<endl; } 按照你上面的规则,应该是60个字节的,但是实际的却是40个字节hairetz 发表于2010年8月30日 12:53:07  IP:219.133.62.*举报回复删除
回复 maoxing63570: long num;//4个字节[24]...[27] float score[3];//12个字节[36].......[47]原则3,则后面还要有[48]...[60] } 问题1:这是你自己写的吗?27后面为何就是36??这么无厘头的逻辑,为何说是按照我上面说的规则? long num;//4个字节[24]...[27] float score[3];//12个字节[28].......[39] 结果就是40。
maoxing63570 发表于2010年8月30日 13:02:28  IP:218.63.63.*举报回复删除
以后每个数据成员存储的起始位置要从该成员大小的整数倍开始,float score[3]成员为12个,那么不应该是从36开始么?hairetz 发表于2010年8月30日 14:54:36  IP:219.133.62.*举报回复删除
回复 maoxing63570:这里的成员要细化到内置类型,所以float score[3]从4字节的整数倍开始即可。
hairetz 发表于2010年8月30日 14:59:17  IP:219.133.62.*举报回复删除
回复 maoxing63570:这个没描述清楚,不好意思。但是如果你注意到规则2的话,应该可以理解,数组要以子成员大小的整数倍开始才是合理的。
maoxing63570 发表于2010年8月30日 15:13:18  IP:218.63.63.*举报回复删除
回复 hairetz:非常感谢你在百忙中给我的回复,网络因你们的热心而精彩
xiaolei_0125 发表于2010年8月31日 14:17:04  IP:221.12.174.*举报回复删除
明显是错误的,一帮人只会跟风。自己编程试一下不就知道啦,在linux上,第一个的sizeof(BB)=16, weight的偏移是4hairetz 发表于2010年8月31日 14:23:51  IP:219.133.62.*举报回复删除
回复 xiaolei_0125:嗯,很好,可惜你用的是gcc,它默认是4,可以认为是#pragma pack(4)的结果。我描述的规则是在不受此规则影响的结果。 VC,VS,codeBlock都是你可以用来试试的编译器。
xiaolei_0125 发表于2010年8月31日 14:26:04  IP:221.12.174.*举报回复删除
哦,刚试过,原来VC不一样,原来不同的编译器处理不一样!呵呵,楼主应该说清楚哦xiaolei_0125 发表于2010年8月31日 14:28:44  IP:221.12.174.*举报回复删除
有点不明白的就是,照楼主说的规则就能提高程序效率吗? 我觉得只要和总线宽度对齐就可以了xiaolei_0125 发表于2010年8月31日 14:48:18  IP:221.12.174.*举报回复删除
终于统一了Crazy_embedded 发表于2010年10月14日 23:31:36  IP:125.90.58.*举报回复删除
谢谢!xiaoyilong19 发表于2011年1月15日 8:56:32  IP:60.208.149.*举报回复删除
收藏了,lz修改过来呗

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值