C 内存对齐

C 内存对齐

对于程序员来说,最烦恼最耗时的工作莫过于与内存打交道,但是内存至关重要,不得不对其保持最大的警惕。

为什么需要内存对齐?

  • 平台原因:不是所有的一欧诺个见平台都能访问任意地址上的任意数据的;某些硬件平台如果访问未对齐的地址,则会报出对齐错误,有些专业的处理器通常不支持访问未对齐地址。
  • 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界对其。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问,而对齐的内存访问仅需要一次。

计算机通常以字大小的块来处理内存,一个字是计算机数据的自然单位,通常由计算机架构决定。现代通用计算机通常是4个字节(32位)或8个字节(64位)。

假设处理器,一次读取4个字节的数据。且内存如图所示

memory_alignment_initial

若保存4字节的int类型,不需要进行额外工作就可以正确对齐,因为int数据类型大小与该架构的数据自然单元契合。

memory_alignment_int_in_memory

若我们放置一个charmemory_alignment_char, 一个shortmemory_alignment_short, 和一个intmemory_alignment_int到内存中。原本应该得到的结果如下所示

memory_alignment_char_short_int

这将需要两次内存访问,并且进行移位来获取int数据。这将比内存对齐的数据至少花费两倍时间,因为计算机科学家们提出了内存对齐的方法。在本例中,添加一些padding在第一个字节,确保有效对齐

memory_alignment_char_short_int_alignment
上图所示被认为是自然对齐,编译器会自动添加正确的padding根据目标平台。

内存对齐规则

默认对齐

如果有指定对齐字节数目,则编译器会按 类或结构中最大类型长度来对齐。可以通过语句#pragma pack(i)来指定对齐字节数目,i的取值为1, 2, 4, 8, 16

对齐规则:

  • 如果设置了内存对齐为 i 字节,类中最大成员对齐字节数为j,那么整体对齐字节n = min(i, j) (某个成员的对齐字节数定义:如果该成员是c++自带类型如int、char、double等,那么其对齐字节数=该类型在内存中所占的字节数;如果该成员是自定义类型如某个class或者struct,那个它的对齐字节数 = 该类型内最大的成员对齐字节数)
  • 每个成员对齐规则:类中第一个数据成员放在offset为0的位置;对于其他的数据成员(假设该数据成员对齐字节数为k),他们放置的起始位置offset应该是 min(k, n) 的整数倍
  • 整体对齐规则:最后整个类的大小应该是n的整数倍
  • 当设置的对齐字节数大于类中最大成员对齐字节数时,这个设置实际上不产生任何效果;当设置对齐字节数为1时,类的大小就是简单的把所有成员大小相加

示例

Example 1

未指定对其字节,则n = 4即最大成员int的大小

struct Foo{
    char x;  // 1 byte  放在偏移为0的地址,位置区间为[0]
    short y;  // 2 bytes  起始位置应该是2的倍数,即2, 位置区间为[2, 3]
    int z;  // 4 bytes  起始位置应为4的倍数,位置区间为[4, 7]
};

此时成员共占用[0-7] 8个字节,还需整体对齐,大小应该是4的倍数,即8

Example 2

假设指定对其字节为8, 那么n = min(8, 8) = 8

struct Foo{
    char x;  // 1 byte 起始位置为0, 位置区间为[0]
    double y;  // 8 bytes 起始位置应为8的倍数,即8, 位置区间为[8, 15]
    char z;  // 1 byte 起始位置应为1的倍数,即16, 位置区间为[16]
}

此时成员共占用17字节,还要整体对齐,为8的倍数,故大小为24

参考

猜你喜欢

转载自blog.csdn.net/u011221820/article/details/79899985