#写在开头, 编译器的对齐值为8byte

  如题, 首先说一下为什么需要字节对齐, 这个似乎有些浪费空间, 那就先上一下比较书面的解释:

    1. 平台要求, 某些CPU只能访问地址为偶数的内存地址, 这个时候你把数据放在奇数地址, 就会报错

    2. 性能要求, 这个跟寄存器有关系, 有些数据不对齐的话需要使用寄存器的次数会增多  

   说点通俗的, 就像一本书, 每页都有字, 但是不一定要写满; 写太满了, 没有章法, 看不了.

  下面, 先说明三个概念:

    1. 自身对齐值: 比如, short为2byte, double为8byte

    2. 指定对齐值: 编译器自己决定的; 或者程序员自己决定的

1 #pragma pack (n)
2 ***
3 ***
4 #pragma pack ()

    那么2, 3行内部的数据指定对齐值便是n

    3. 有效对齐值: 上面两个值的最小值

  对齐的规则呢:  

    1. 数据本身存储的地址应该是, 数据本身有效对齐值的整数倍

    2. 对于结构体而言, 由于1这条规则, 以及有结构体数组的存在, 所以结构体本身的有效对齐值为内部             数据有效对齐值的最大值.

  这么说还是比较抽象, 我们上点例子

1 struct A {
2    char a;
3    int b;
4 };

首先a的自身对齐值为1, 制定对齐值为8, 那么有效对齐值为min{1, 8} = 1

  0x0    0x1    0x2    0x3  
   a   

接下来, 需要把b放进去, 他的地址应该时自身有效对齐为min{4, 8} = 4, 所以0x1, 0x2, 0x3都存不了,得从0x4开始存储

  0x0    0x1    0x2    0x3    0x4    0x5    0x6    0x7  
   a      b   b   b   b

如上, 很简单对吧, 下面讲一下规则2, 给结构体A加个char c

1 struct A {
2    char a;
3    int b;
4    char c;
5 };

那么有趣的事情就会发生, a的有效对齐值为1, b的为4, c的为1, 那么A的就是4, 所以c的后面3个单元会被占用, 想一想, 如果不这样补齐的话, 你想想对于结构体数组的第二个元素的b他的地址就不是4的整数倍了.

 0x0  0x1  0x2  0x3  0x4  0x5  0x6  0x7  0x8  0x9  0xA  0xB 
  a     b  b  b  b  c    

所以, 究其根本, 所有的一切都是为了满足第1条规则.

那么再来如果把b换成double类型的呢?

1 struct A {
2    char a;
3    double b;
4    char c;
5 };

那么a的有效对齐值为1, b为8, c为1, A为8, 那么整体应该占24byte

 0x0  0x1  0x2  0x3  0x4  0x5  0x6  0x7  0x8  0x9  0xA  0xB  0xC  0xD  0xE  0xF  0x10  0x11  0x12  0x13  0x14  0x15  0x16  0x17 
  a         b  b  b  b  b  b  b  b  c       

光说不练假把式, 我们上几道题目

 1 #pragma pack (4)
 2 struct S1 {
 3     char a;
 4     double b;
 5 };
 6
 7 struct S2 {
 8     char a;
 9     S1 s;
10     char b;
11     char c;
12 };
13
14 struct S3 {
15     int a;
16     char b[10];
17 };
18 #pragma pack ()

注意指定对齐值被改为了4, 拿自己的IDE试试吧!

有问题, 请联系[email protected]

01-22 19:19