C语言结构体对齐也是老生常谈的话题了。基本上是面试题的必考题。内容虽然很基础,但一不小心就会弄错。写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢?

开始学的时候,也被此类问题困扰很久。其实相关的文章很多,感觉说清楚的不多。结构体到底怎样对齐?

有人给对齐原则做过总结,具体在哪里看到现在已记不起来,这里引用一下前人的经验(在没有#pragma pack宏的情况下):

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

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

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

这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。

例1:struct{
                     short a1;
                     short a2;
                     short a3;
                    }A;

struct{
                   long a1;
                   short a2;
                  }B;

sizeof(A) = 6; 这个很好理解,三个short都为2。

sizeof(B) = 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。

例2:struct A{
                    int a;
                    char b;
                    short c;
                    };

struct B{
                   char b;
                   int a;
                   short c;
                    };

sizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。

sizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。

深究一下,为什么是这样,我们可以看看内存里的布局情况。

a         b         c
       A的内存布局:1111,     1*,       11

b          a        c
       B的内存布局:1***,     1111,   11**

其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。

B中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。

再看一个结构中含有结构成员的例子:

例3:struct A{
                     int a;
                     double b;
                     float c;
                    };

struct B{
                     char e[2];
                     int f;
                     double g;  
                     short h;
                     struct A i;
                    };

sizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。

sizeof(B) = 48; 看看B的内存布局。

e         f             g                h                                    i
       B的内存布局:11* *,   1111,   11111111, 11 * * * * * *,        1111* * * *, 11111111, 1111 * * * *

i其实就是A的内存布局。i的起始位置要为8的倍数,因为A中最大元素为8。所以h后面要补齐。把B的内存布局弄清楚,有关结构体的对齐方式基本就算掌握了。

以上讲的都是没有#pragma pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义来。比如上面的结构体前加#pragma pack(1),内存的布局就会完全改变。sizeof(A) = 16; sizeof(B) = 32;

有了#pragma pack(1),内存不会再遵循原则1和原则3了,按1字节对齐。没错,这不是理想中的没有内存对齐的世界吗。

a                b             c
       A的内存布局:1111,     11111111,   1111

e        f             g          h                     i
       B的内存布局:11,   1111,   11111111, 11 ,            1111, 11111111, 1111

那#pragma pack(2)的结果又是多少呢?#pragma pack(4)呢?留给大家自己思考吧,相信没有问题。

还有一种常见的情况,结构体中含位域字段。位域成员不能单独被取sizeof值。C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。

使用位域的主要目的是压缩存储,其大致规则为:
       1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
       2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
       3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;
       4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
       5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。

还是让我们来看看例子。

例4:struct A{
                      char f1 : 3;
                     char f2 : 4;
                     char f3 : 5;
                     };

a         b             c
       A的内存布局:111,    1111 *,   11111 * * *

位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(A)的结果为2。

例5:struct B{
                    char f1 : 3;
                    short f2 : 4;
                    char f3 : 5;
                    };

由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。

例6:struct C{
                     char f1 : 3;
                     char f2;
                    char f3 : 5;
                    };

非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。

考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。

最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间。

例3的理解有问题,struct A和struct B都是按8字节对齐的
如果定义修改成:
struct A {
   int a;
   double b;
   float c;
   };
struct B{
   short h;
   struct A i;
   };

按楼主的说法,sizeof(struct B)还是应该等于48,而实际上在VC++里面看到的值是:sizeof(struct A)=24,sizeof(struct B)=32。
应该来说当没有设置#pragma pack(n)的时候,按自然默认对齐方式,所以struct A应该按8字节对齐,而struct B中成员short h按2自己对齐,成员struct A i按8自己对齐,所以最终struct B应该按8字节对齐,所以sizeof(struct B)=32,而不是48。

多谢楼上的提醒。

struct B 确实不是按24字节对齐,而是按8字节对齐。

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

C语言结构体对齐[转]相关推荐

  1. C语言结构体对齐的不足

    该博文为原创文章,未经博主同意不得转载,如同意转载请注明博文出处 本文章博客地址:https://cplusplus.blog.csdn.net/article/details/105065657 C ...

  2. C语言结构体对齐详解

    文章目录 一.C语言结构体对齐大小快速判断 二.反汇编角度看结构体 三.总结 一.C语言结构体对齐大小快速判断 在C语言中定义一个结构体,里面具体占用多少个字节呢,先举一个例子,如下: #includ ...

  3. linux windows 结构体,Linux下C语言——结构体对齐

    结构体对齐的步骤: 1.结构体各成员对齐 2.整个结构体圆整 结构体对齐的特定对齐值: 1.自身对齐值: 自身对齐值就是结构体变量里每个成员的自身大小; 2.指定对齐值: 指定对齐值是由宏#pragm ...

  4. C语言结构体对齐,超详细,超易懂

    结构体对齐规则 结构体的第一个成员总是存放在结构体变量开辟的空间的起始地址 其它成员变量要存储在一个名叫对齐数的整数倍的地址 结构体总大小为最大对齐数的整数倍,结构体内每一个变量都会产生一个对齐数,取 ...

  5. Linux+c语言结构体对齐,C语言中结构体struct的对齐问题解析

    一:struct和union的区别 struct,相互关联的元素的集合,每个元素都有自己的内存空间:每个元素在内存中的存放是有先后顺序的,就是定义时候的顺序:一个struct所占的总的内存大小,并不是 ...

  6. C语言结构体的大小 — — 内存对齐和位域

    C语言结构体对齐 C语言结构体对齐也是老生常谈的话题了.基本上是面试题的必考题.内容虽然很基础,但一不小心就会弄错.写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结 ...

  7. c语言结构体和联合体,C语言结构体和联合体

    1.单链表插入 #include #include #define FALSE 0 #define TRUE 1 typedef struct NODE{ STRUCT NODE *link; int ...

  8. C语言之结构体以及结构体对齐访问

    1:简单理解,结构体就是数组的进一步发展,数据的优点和缺陷在于数据里面是元素类型必须相同,但是结构体没有这个要求,结构体里面元素的类型可以相同也可以不同. 2:结构体的定义: struct stude ...

  9. C语言结构体-大小,对齐,填充,使用及其他

    C语言结构体-大小,对齐 C语言中的结构体(struct)的定义 在C语言中,最常用的数据结构就是结构体了,结构体也是其它数据结构(比如链表等)的基础,结构体的使用非常简单. 比如,定义一个结构体: ...

最新文章

  1. collectors排序_Collectors.groupingBy分组后的排序问题
  2. ARM Cortex-A 编程手册学习笔记
  3. Hadoop生态Flume(二)安装配置
  4. c++面向对象高级编程 学习十二 模板
  5. 新手学信息检索4:向量空间模型与相似度计算
  6. 企业如何考虑自己的网络防护设备
  7. shell取mysql数据_通过shell读取mysql数据——20120417
  8. 打磨锤子计算机专业,工大金课 | 在北工大校园里,磨一把“工大锤”
  9. Linux首次登陆设置root
  10. HTML之组件margin、padding
  11. 4.闭锁 CountDownLatch
  12. APNs Push Notification教程一
  13. STC 32位8051单片机开发实例教程 一 开发环境搭建
  14. This property is obsolete now. Use the TextPattern property instead.的警告解决办法
  15. UGUI中UI控件简介
  16. cousera-usable security
  17. 后台获取用户登录信息
  18. HTML_水平线详解
  19. 企业微信机器人脚本python_Python实现企业微信机器人每天定时发消息实例
  20. 武汉软件工程职业学院计算机分数线,武汉软件工程职业学院录取分数线2021是多少分(附历年录取分数线)...

热门文章

  1. 小区移动基站安全吗?会杀人于无形吗?
  2. EMC测试中骚扰源头的寻找
  3. 我是技术男,也曾创过业,也曾拿过风投...
  4. 售价150万的“AI老婆”,上市仅1小时就被抢光
  5. 【干货】10张GIF动图让你弄懂循环、递归、二分检索等概念
  6. sublime text下 Python 问题:TabError: inconsistent use of tabs and spaces in indentation
  7. 串口(USART)通信-串口通讯协议简介
  8. Web开发——PHP vs Java
  9. MFC开发-垂直滚动条一直处于底部
  10. 三星Galaxy S22 Ultra更多细节:采用Note系列外观因素