转载

关于结构体字对齐

在我们声明一个结构体是,我们可以在同一结构体中声明不同数据类型的成员变量,例如:

struct {
    char a;    //1byte
    int b;    //4byte
    char c[2]    //2byte
    double d;    //8byte
}Struct_A;

在计算机内存中,结构体变量的存储通常是按字长对齐的。


比如8位机里就按1字节对齐,那么上述结构体共占用:


1+4+2+8=15 byte


在16位机里,变量就按照2字节对齐,比如a这个成员,虽然是个char类型,地址在0x80000000本身只占1字节,


但是下一个成员b却不能使用0x80000001这个


地址,而必须使用0x80000002,这就是按字长对齐。以上结构体占用的空间也就是:


2+4+2+8=16 byte


同理,在32位机中,变量就按照4字节对齐a在0x80000000的话,b只能放在0x80000004,因为这里的字长是4


个字节。以上结构体占用空间:


4+4+4+8=20 byte


也就是说总有一些字节是浪费掉的,这样做的目的很简单,就是因为在大多数计算机体系结构中,对内存操作时按


整字存取才能达到最高效率,相当于是以空


间换取时间


当然在某些计算机体系结构中,比如ARM,是支持非对齐字传输的,也就是说变量并不一定要按照字长对齐,尽


管这样可能会降低效率,但换来的是存储空间


上的节约。对于程序员来讲,则需要将结构体声明为紧凑型结构体。声明的关键字依编译器不同而异,你可以去查


一下__packed关键字,可以得到更详细的说


明。使用紧凑型结构体,则会强制编译器将结构体成员按1字节对齐,则以上结构体占用空间仍为15字节。


/**********************************以下是关于结构体对齐的内容【转载来的】*******************************/

(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 则先补空字节直至这个表达式成立;

  (二)结构体的长度必须为结构体的自身对齐值的整数倍,不够就补空字节。

 

  举个例子:

#pragmapack(8)
structA{
  chara;
  longb;
};
structB{
  chara;
  structAb;
  longc;
};

  对于 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。那么structB 在内存中的顺序步骤为:

  (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占四位)

文章最后发布于: 2016-02-28 20:49:11
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览