【问题标题】:Memory alignment, structs and malloc内存对齐、结构和 malloc
【发布时间】:2014-10-29 01:22:20
【问题描述】:
在一个问题中表达我想知道的内容有点困难,所以我会尝试分解它。
例如,假设我们有以下结构:
struct X {
uint8_t a;
uint16_t b;
uint32_t c;
};
是否保证编译器永远不会重新排列 X 成员的顺序,只在必要时添加填充?换句话说,offsetof(X, a)
编译器是否会在 X 的成员中选择最大对齐并使用它来对齐 X 类型的对象(即 X 实例的地址将被 X 成员中的最大对齐整除)?
由于 malloc 在分配缓冲区时不知道要存储的对象的类型,它如何为返回的地址选择对齐方式?它是否只是返回一个可以被最大对齐可能整除的地址(在这种情况下,无论我们在缓冲区中放置什么结构,内存访问都将始终对齐)?
【问题讨论】:
标签:
c++
c
memory-management
malloc
memory-alignment
【解决方案1】:
- 是的
- 不,编译器将使用其对目标主机硬件的了解来选择最佳对齐方式。
- 见问题 2。
【解决方案2】:
由于 malloc 在分配缓冲区时不知道要存储的对象的类型,它如何为返回的地址选择对齐方式?
malloc(3) 返回“为任何类型的变量适当对齐的内存。”
它是否只是简单地返回一个可被最大对齐可能整除的地址(在这种情况下,无论我们在缓冲区中放置什么结构,内存访问都将始终对齐)?
是的,但请注意您是否遵守the strict aliasing rule。
【解决方案3】:
编译器会在最多的情况下对该计算机执行最有益的操作。在大多数平台上,在总线宽度偏移上加载总线宽度值是最快的。
这意味着通常在 32 位计算机上,编译器会选择在 4 字节偏移量上对齐 32 位数字。在 64 位计算机上,64 位值在 8 个字节偏移上对齐。
在大多数计算机上,较小的值(例如 8 位和 16 位值)加载速度较慢。可能它周围的所有 4 或 8 个字节都已加载,并且您需要的字节或两个字节都被屏蔽掉了。
当您有特殊情况时,您可以通过指定对齐和填充来覆盖编译器。当您知道快速加载并不重要,但您确实想要紧密地打包数据时,您可能会这样做。或者当您在使用强制转换和联合时玩非常微妙的技巧时。
几乎所有现代计算机上的内存分配例程将始终返回至少在平台的总线宽度(例如 4 或 8 字节)上对齐的内存 - 甚至更多 - 如 16 字节对齐。
当您调用“malloc”时,您有责任了解所需结构的大小。幸运的是,编译器会用“sizeof”告诉你任何结构的大小。这意味着如果您打包一个结构以节省内存,sizeof 将返回一个比未打包的结构更小的值。所以你真的会节省内存 - 如果你在它们的大数组中分配小结构。
如果您一次分配一个小型打包结构 - 那么是的 - 无论您是否打包它们都不会产生任何影响。那是因为当您分配一些奇怪的小块内存时 - 分配器实际上会使用比这更多的内存。它将为您分配一个方便大小的内存块,然后为自己分配一个额外的内存块以跟踪您的分配。
这就是为什么如果您关心内存使用并想要打包您的结构 - 您绝对不想一次分配一个。