攀升·Uranus


Something Different,Something New
数据加载中……

结构体对齐那点事 熟练c/c++(四)

          刚刚完成一个文件的迁移程序,其中遇到了结构体对齐的问题,所以拿出来说说,与各位博友们分享。

我的程序很简单,就是把之前通过一个结构体fwrite到文件A里的内容读出,然后转给另一个结构体保存。程序是简单,但我担心的是之前结构体fwrite到文件A的程序对齐结构体规则是怎样的?一定要知道它吗? 当然了,如果那个程序结构体是按照1对齐写入的,我的程序结构体是按照4对齐读入,那不就糟了!

      这里我引入结构体对齐的概念,也可以说是内存对齐了。为什么要内存对齐呢,就是方便CPU寻址了,具体原因大家要参考计算机体系结构了。先看一个内存对齐的例子:

      struct example1{

           char a;

           double b;

           long l;

};

      struct example2{

           char a;

           long l;

           double b;

};

大家算算结构体大小,初次接触的博友可能对答案有点惊讶,VC编译, sizeof后结果分别是:2416 同样是的结构体,成员换了顺序,大小就不同了。其实内存对齐有个规则,只要知道了,就OK。那么以下5点是关键

1.          内存对齐与编译器设置有关,首先要搞清编译器这个默认值是多少

2.          如果不想编译器默认的话,可以通过#pragma pack(n)来指定按照n对齐

3.          每个结构体变量对齐,如果对齐参数n(编译器默认或者通过pragma指定)大于该变量所占字节数(m),那么就按照m对齐,内存偏移后的地址是m的倍数,否则是按照n对齐,内存偏移后的地址是n的倍数。也就是最小化长度规则

4.          结构体总大小: 对齐后的长度必须是成员中最大的对齐参数的整数倍。最大对齐参数是从第三步得到的。

5.          补充:如果结构体A中还要结构体B,那么B的对齐方式是选它里面最长的成员的对齐方式

所以计算结构体大小要走三步,首先确定是当前程序按照几对齐(参照12),接着计算每个结构体变量的大小和偏移(参照35),最后计算结构体总大小(参照4)。

      先算算example1吧,假设编译器是以16对齐的

      1.确定按照几对齐: 16;

      2.确定每个成员的偏移:a 占一个字节,16>1, 按照1对齐,起始位置00%1 = 0,那么a就存在0位置;b8个字节,16>8,按照8对齐,起始位置就不能是1了,因为要按照8对齐,所以最近的偏移起始位置是8 8%8 =0, 那么b就存在位置8-15的位置;l4个字节,16>4,按照4对齐,起始位置16 16%4=0,那么l就存在位置16-19的位置。所以结构体从019一共占用20个字节

      3.结构体总大小:成员中最大的对齐参数是b8对齐,所以208!=0, 24刚好。

      真的很搞!同理计算example2应该是16

     再举个结构体嵌套的例子吧,

#pragma pack(push)

#pragma pack(8)

struct test1{

      int a;

      char b;

      int c[20]

long l;

} ;

struct test2{

      char a1;

      char a2;

      struct test1 t1;

      double b1;

}

#pragma pack(pop)

先计算test1, 8对齐,a占用0-3b占用4c占用887l占用8891,一共92个字节。成员中最大的对齐参数是int92%4=0;

再计算test2, a1z占用0a2占用1t1呢,4 % 4 (test1里面最长的成员的对齐方式) = 0, 4-95b196103;一共104个字节,成员中最大的对齐参数是double104%8=0; 所以是104.


      那关于我文章开头提到的那个文件转换,我现在只要知道原始程序是按照什么对齐的,然后在新程序中指定按照几对齐就可以了,哈哈!            
      
      挤时间写的,
有的地方有遗漏,请各位指正!

posted on 2009-01-06 23:39 攀升 阅读(8837) 评论(9)  编辑 收藏 引用 所属分类: C/C++

评论

# re: 结构体对齐那点事 熟练c/c++(四)   回复  更多评论   

恩. 很有道理
2009-01-07 11:13 | li_young

# re: 结构体对齐那点事 熟练c/c++(四)   回复  更多评论   

在gcc下example1和example2都是16
2009-01-07 12:53 | ronliu

# re: 结构体对齐那点事 熟练c/c++(四)   回复  更多评论   

在gcc下example1和example2都是16 中默认字节对齐为4 所以都是16
2009-01-07 16:57 | feosun

# re: 结构体对齐那点事 熟练c/c++(四)   回复  更多评论   

@ronliu
博友feosun告诉了你答案
所以为了防止不同编译器对齐不一样,我建议在代码里面指定对齐参数
#pragma pack(n)
2009-01-07 18:14 | 攀升

# re: 结构体对齐那点事 熟练c/c++(四)   回复  更多评论   

内存对齐一直是很热的话题啊。。呵呵。。。
2009-01-08 08:38 | guest

# re: 结构体对齐那点事 熟练c/c++(四)   回复  更多评论   

有个地方没太看懂
就是嵌套结构体时,如何决定嵌套结构体的首地址?即文中"t1呢,92%2!=0, 2-92"这一段,不是说考虑结构体大小时,应该为其成员的最大值么?
2009-08-31 10:54 | 游客

# re: 结构体对齐那点事 熟练c/c++(四)   回复  更多评论   

能否把我给你的评论(最前面的两条中有)删掉,我的留言中使用了邮件地址,收到了无数的垃圾邮件,烦都烦死了。如果可以,真的不胜感激
2009-10-16 22:09 | 你好!

# re: 结构体对齐那点事 熟练c/c++(四)   回复  更多评论   

@你好!
我帮你处理了,哈哈
2010-01-14 23:12 | 攀升

# re: 结构体对齐那点事 熟练c/c++(四)   回复  更多评论   

@游客
>有个地方没太看懂
>就是嵌套结构体时,如何决定嵌套结构体的首地址?即文中"t1呢,92%2!>=0, 2-92"这一段,不是说考虑结构体大小时,应该为其成员的最大值么?

谢谢你的提醒,我改好了,呵呵
2010-01-14 23:24 | 攀升

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理