关于C语言中的结构体对齐

原创 2008年10月02日 15:58:00

(1)什么是字节对齐
    一个变量占用 n 个字节,则该变量的起始地址必须能够被 n 整除,即: 存放起始地址 % n = 0, 对于结构体而言,这个 n 取其成员种的数据类型占空间的值最大的那个。
(2)为什么要字节对齐
    内存空间是按照字节来划分的,从理论上说对内存空间的访问可以从任何地址开始,但是在实际上不同架构的CPU为了提高访问内存的速度,就规定了对于某些类型的数据只能从特定的起始位置开始访问。这样就决定了各种数据类型只能按照相应的规则在内存空间中存放,而不能一个接一个的顺序排列。

    举个例子,比如有些平台访问内存地址都从偶数地址开始,对于一个int型(假设32位系统),如果从偶数地址开始的地方存放,这样一个读周期就可以读出这个int数据,但是如果从奇数地址开始的地址存放,就需要两个读周期,并对两次读出的结果的高低字节进行拼凑才能得到这个int数据,这样明显降低了读取的效率。
(3)如何进行字节对齐

    每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(不指定则取默认值)中较小的一个对齐,并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节。

    这个规则有点苦涩,可以把这个规则分解一下,前半句的意思先获得对齐值后与指定对齐值进行比较,其中对齐值获得方式如下:

1. 数据类型的自身对齐值为:对于char型数据,其自身对齐值为1,对于short型为2,对于int, long, float类型,其自身对齐值为4,对于 double 类型其自身对齐值为8,单位为字节。
2.结构体自身对齐值:其成员中自身对齐值最大的那个值。

其中指定对齐值获得方式如下:

#pragma pack (value)时的指定对齐值value。

未指定则取默认值。

    后半句的意思是主要是针对于结构体的长度而言,因为针对数据类型的成员,它仅有一个对齐参数,其本身的长度、于这个对齐参数,即1倍。对于结构体而言,它可能使用了多种数据类型,那么这句话翻译成对齐规则: 每个成员的起始地址 % 自身对齐值 = 0,如果不等于 0 则先补空字节直至这个表达式成立。
    换句话说,对于结构体而言,结构体在在内存的存放顺序用如下规则即可映射出来:
    (一)每个成员的起始地址 % 每个成员的自身对齐值 = 0,如果不等于 0 则先补空字节直至这个表达式成立;
    (二)结构体的长度必须为结构体的自身对齐值的整数倍,不够就补空字节。

    举个例子:

  1. #pragma pack(8)
  2. struct A{
  3.     char a;
  4.     long b;
  5. };
  6. struct B{
  7.     char a;
  8.     struct A b;
  9.     long c;
  10. };
  11. struct C{
  12.     char a;
  13.     struct A b;
  14.     double c;
  15. };
  16. struct D{
  17.     char a;
  18.     struct A b;
  19.     double c;
  20.     int d;
  21. };
  22. struct E{
  23.     char a;
  24.     int b;
  25.     struct A c;
  26.     double d;    
  27. };

    对于 struct A 来说,对于char型数据,其自身对齐值为1,对于long类型,其自身对齐值为4, 结构体的自身对齐值取其成员最大的对齐值,即大小4。那么struct A 在内存中的顺序步骤为:
(1) char a, 地址范围为0x0000~0x0000,起始地址为0x0000,满足 0x0000 % 1 = 0,这个成员字节对齐了。
(2) long b, 地址起始位置不能从0x00001开始,因为 0x0001 % 4 != 0,  所以先补空字节,直到0x00003结束,即补3个字节的空字节,从0x00004开始存放b,其地址范围为0x00004~0x0007.
(3)此时成员都存放结束,结构体长度为8,为结构体自身对齐值的2倍,符合条件(二).
    此时满足条件(一)和条件(二),struct A 中各成员在内存中的位置为:a*** b ,sizeof(struct A) = 8。(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,b占四位)

    对于struct B,里面有个类型为struct A的成员b自身对齐值为4,对于long类型,其自身对齐值为4. 故struct B的自身对齐值为4。那么struct B 在内存中的顺序步骤为:
(1) char a, 地址范围为0x0000~0x0000,起始地址为0x0000,满足 0x0000 % 1 = 0,这个成员字节对齐了。
(2) struct A b, 地址起始位置不能从0x00001开始,因为 0x0001 % 4 != 0,  所以先补空字节,直到0x00003结束,即补3个字节的空字节,从0x00004开始存放b,其地址范围为0x00004~0x00011.
(3) long c,地址起始位置从0x000012开始, 因为 0x0012 % 4 = 0,其地址范围为0x00012~0x0015.
(4)此时成员都存放结束,结构体长度为16,为结构体自身对齐值的4倍,符合条件(二).
    此时满足条件(一)和条件(二),struct B 中各成员在内存中的位置为:a*** b c ,sizeof(struct C) = 24。(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,b占八位,c占四位)
    对于struct C,里面有个类型为struct A的成员b自身对齐值为4,对于double 类型,其自身对齐值为8. 故struct C的自身对齐值为8。那么struct C 在内存中的顺序步骤为:
(1) char a, 地址范围为0x0000~0x0000,起始地址为0x0000,满足 0x0000 % 1 = 0,这个成员字节对齐了。
(2) struct A b, 地址起始位置不能从0x00001开始,因为 0x0001 % 4 != 0,  所以先补空字节,直到0x00003结束,即补3个字节的空字节,从0x00004开始存放b,其地址范围为0x00004~0x00011.
(3) double c,地址起始位置不能从0x000012开始, 因为 0x0012 % 8 != 0,所以先补空字节,直到0x000015结束,即补4个字节的空字节,从0x00016开始存放c,其地址范围为0x00016~0x0023.
(4)此时成员都存放结束,结构体长度为24,为结构体自身对齐值的3倍,符合条件(二).
    此时满足条件(一)和条件(二),struct C 中各成员在内存中的位置为:a*** b **** c ,sizeof(struct C) = 24。(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,b占八位,c占八位)

    对于struct D,自身对齐值为8。前面三个成员与 struct C 是一致的。对于第四成员d,因为 0x0024 % 4 = 0, 所以可以从0x0024开始存放d, 其地址范围为0x00024~0x00027.此时成员都存放结束,结构体长度为28,28 不是结构体自身对齐值8的倍数,所以要在后面补四个空格,即在0x0028~0x0031上补四个空格。补完了,结构体长度为32, 为结构体自

身对齐值的4被,,符合条件(二).
    此时满足条件(一)和条件(二),struct D 中各成员在内存中的位置为:a*** b **** c d **** ,sizeof(struct D) = 32。(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,b占八位,c占八位, d占四位)。

    对于struct E 中各成员在内存中的位置为:a*** b c d, sizeof(struct E) = 24。(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,b占四位,c占八位, d占八位)。
    通过struct D 和 struct E 可以看出,在成员数量和类型一致的情况,后者的所占空间少于前者,因为后者的填充空字节要少。如果我们在编程时考虑节约空间的话,应该遵循将变量按照类型大小从小到大声明的原则, 这样尽量减少填补空间。另外,可以在填充空字节的地方来插入reserved成员, 例如
struct A
{
   char a;
   char reserved[3];
   int b;
};
    这样做的目的主要是为了对程序员起一个提示作用,如果不加则编译器会自动补齐。

 

引用:

[1 ] http://blog.ednchina.com/sealove518/55803/message.aspx

 

 

C语言结构体的字节对齐原则

为什么要对齐?   现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数...
  • shenbin1430
  • shenbin1430
  • 2009年06月23日 18:27
  • 13533

C语言字节对齐、结构体对齐最详细的解释

文章最后本人做了一幅图,一看就明白了,这个问题网上讲的不少,但是都没有把问题说透。   一、概念       对齐跟数据在内存中的位置有关。如果一个变量的内存地址正好位于它长度的整数倍,他就...
  • lanzhihui_10086
  • lanzhihui_10086
  • 2015年03月17日 21:57
  • 10501

C语言结构体对齐与不对齐设置总结

参考: 相信不同的编译平台间的默认设置差异给大家带来了很多困扰。在此,仅就结构体对齐解析下之间的差异设置方法。 1.gcc中结构体默认是4个字节对齐,即为32的倍数。 1.1修改字节...
  • gll028
  • gll028
  • 2014年10月08日 17:33
  • 2762

C语言内存中结构体对齐分析

一、字节对齐作用和原因: 对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问一个没有进行对齐的变量的时候会...
  • yusiguyuan
  • yusiguyuan
  • 2014年04月07日 14:43
  • 1673

30.C语言结构体对齐访问

30.1.C语言之结构体概述 30.2.从数组到结构体的进化 30.3.访问结构体和数组中的元素 30.4.结构体对齐访问的来龙去脉 30.5.结构体对齐的规则和运算 30.6.gcc支持但不推荐的对...
  • Rston
  • Rston
  • 2016年04月04日 13:05
  • 788

C语言结构中的边界对齐问题

没错,今天2月14,单身狗如我情人节也只能苦逼地撸代码了。不知道大家有没有发现一个奇怪的现象,见图~ 结构test里面包含了1个int型变量和2个char型变量,照理说sizeof...
  • qq_37521235
  • qq_37521235
  • 2017年02月14日 23:56
  • 775

C语言结构体对齐问题详解

C语言结构体对齐问题详解 测试环境32位机 WinXP: 编译器VC6(MS cl.exe ) 和 mingw32-gcc-4.5.2  1 结构体数据对齐(没有#pragma pack...
  • tiany524
  • tiany524
  • 2011年04月01日 20:33
  • 7351

关于C语言中的结构体对齐

(1)什么是字节对齐     一个变量占用 n 个字节,则该变量的起始地址必须能够被 n 整除,即: 存放起始地址 % n = 0, 对于结构体而言,这个 n 取其成员种的数据类型占空间的值最大的那个...
  • wuhuan_001
  • wuhuan_001
  • 2013年12月28日 15:21
  • 361

[转]关于C语言中的结构体对齐

对结构体内存对齐规则和长度规则做了具体的总结,而且描述了具体的伪算法。...
  • DLite
  • DLite
  • 2011年02月12日 21:05
  • 1013

面试必考 - C语言结构体对齐

C语言结构体对齐也是老生常谈的话题了。基本上是面试题的必考题。内容虽然很基础,但一不小心就会弄错。写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的...
  • wze2009
  • wze2009
  • 2010年04月06日 21:52
  • 704
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:关于C语言中的结构体对齐
举报原因:
原因补充:

(最多只允许输入30个字)