内存对齐、structs和malloc

Memory alignment, structs and malloc

本文关键字:malloc structs 对齐 内存      更新时间:2023-10-16

在一个问题中表达我想知道的内容有点困难,所以我会尝试将其分解。

例如,假设我们有以下结构:

struct X {
  uint8_t a;
  uint16_t b;
  uint32_t c;
};
  1. 编译器保证永远不会重新排列X成员的顺序,只在必要时添加填充,这是真的吗?换句话说,offsetof(X,a)<offsetof(X,c)?

  2. 编译器会在X的成员中选择最大的对齐方式,并使用它来对齐X类型的对象(即,X实例的地址可以被X成员中最大的对齐次数整除),这是真的吗?

  3. 由于malloc在分配缓冲区时对我们要存储的对象类型一无所知,它如何为返回的地址选择对齐方式?它是否只是返回一个地址,该地址可以被最大的对齐整除(在这种情况下,无论我们在缓冲区中放入什么结构,内存访问都将始终对齐)?

  1. 不,编译器将使用其对目标主机硬件的了解来选择最佳对齐
  2. 参见问题2

由于malloc在分配缓冲区时对我们要存储的对象类型一无所知,它如何为返回的地址选择对齐方式?

malloc(3)返回"为任何类型的变量适当对齐的内存。"

它是否只是返回一个地址,该地址可以被最大的对齐整除(在这种情况下,无论我们在缓冲区中放入什么结构,内存访问都将始终对齐)?

是的,但要注意遵守严格的别名规则。

编译器将在最多的情况下在该计算机上执行任何最有益的操作。在大多数平台上,在总线宽度偏移上加载总线宽度值是最快的。

这意味着,通常在32位计算机上,编译器会选择在4字节偏移量上对齐32位数字。在64位计算机上,64位值在8字节偏移上对齐。

在大多数计算机上,较小的值(如8位和16位值)加载较慢。可能它周围的所有4或8个字节都已加载,您需要的字节或两个字节被屏蔽

当您有特殊情况时,您可以通过指定对齐和填充来覆盖编译器。当你知道快速加载并不重要,但你真的想把数据打包时,你可能会这样做。或者当你在选角和工会上玩非常微妙的把戏时。

几乎任何现代计算机上的内存分配例程都会返回至少在平台的总线宽度上对齐的内存(例如4或8字节),甚至更像16字节对齐。

当你调用"malloc"时,你有责任知道你需要的结构的大小。幸运的是,编译器会用"sizeof"告诉你任何结构的大小。这意味着,如果您打包一个结构以节省内存,sizeof将返回比未打包结构更小的值。因此,如果您在它们的大数组中分配小结构,您确实会节省内存。

如果你一次分配一个小的打包结构——那么是的——如果你打包与否,不会有任何区别。这是因为当您分配一些奇怪的小块内存时,分配器实际上会使用比这多得多的内存。它会为您分配一个方便大小的内存块,然后为自己分配一个额外的内存块来跟踪您的分配情况。

这就是为什么如果你关心内存使用并想打包你的结构——你肯定不想一次分配一个。