结构体、类在内存中的字节对齐问题

时间:2020-12-28 19:05:08

  所有变量在内存中的存储都有对齐问题,包括结构体和类(对象)等,只是这些对程序员是透明的,不需要关心。说白了,对齐就是在空间与复杂度上达到平衡,在可接受的空间浪费前提下,尽可能提高相同运算过程的速度

  举个例子,现在有两个变量:char A; int B; 在不考虑对齐时,A变量从内存地址0开始分配,占0x00内存,B变量占0x01-0x04。一般情况下,地址总线总是按照对齐后的地址来访问的。计算机的处理过程是先将0x00-0x03共32位读入寄存器,与0x000000FF做与运算或者通过左移24位再右移24位得到A的值。获得B变量的方法是将0x00-0x03这32位读入寄存器,得到低24位的值,再取0x04-0x07这32位读入寄存器得到高8位的值,再与先得到的24位做位运算,即可得到32位的值。可以看到B本身是32位数,却分成2部分再合并,效率较低,但是如果地址一开始就是对齐的,则系统只要一次读写。因此考虑性能方面,编译器会对变量进行对齐处理,以几个字节浪费为代价获得较快的处理速度。所以改进方法是使A占0x00内存,另外0x01-0x03不存数据,B变量占0x04-0x07内存。

  那么结构体或类成员的对齐方式如何呢?为了便捷加工连续多个相同类型原始变量,同时简化原始变量寻址,并且考虑最少处理原则,因此一般将原始变量的长度作为针对此变量的分配单位,比如内存可用64个单位,如果某原始变量长度为8字节,即使机器字长为4字节,分配的时候也以8字节对齐,这样寻址和分配时均按8字节为单位进行,使效率更高。

  系统默认的对齐规则,1.变量的最高效加工,2.达到最少内存空间。

  结构体举例如下:

  typedef struct T
  {
      char c; //本身长度1字节
      __int64 d;  //本身长度8字节
      int e;  //本身长度4字节
      short f;  //本身长度2字节
      char g;  //本身长度1字节
      short h;  //本身长度2字节
  }C;

  按照上述变量的内存分配方式给这个结构体分配完内存空间,但如果定义一个结构体数组C arr[2],按变量分配原则后,并不能直接将arr[1]接在arr[0]后,因为这会使得arr[1]的很多成员不再对齐,原因就是arr[1]开始边界不对齐。解决的方案则是保证结构体长度是原始成员最长分配的整数倍即可,即给arr[0]中变量分配完后根据最长成员对齐,即与8字节对齐,所以在h变量后增加空白内存以补齐得到最长成员内存整数倍,使得arr[1]开始的内存为最大分配内存的整数倍。具体存储如下图:

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

c

pad

d

e

f

g

pad

h

结构体pad

c

pad

d

e

f

g

pad

h

结构体pad