在一个问题中制定我想知道的内容有点困难,所以我会尝试将其分解。
例如,假设我们有以下结构:
struct X {
uint8_t a;
uint16_t b;
uint32_t c;
};
是否确保编译器永远不会重新排列X成员的顺序,只在必要时添加填充?换句话说,偏移(X,a)< offsetof(X,c)?
编译器是否会选择X成员中最大的对齐并使用它来对齐X类型的对象(即X实例的地址将被X成员中最大的对齐整除)?
由于malloc在分配缓冲区时对我们要存储的对象类型一无所知,它如何选择返回地址的对齐方式?它是否只返回一个可被最大对齐整除的地址(在这种情况下,无论我们放入缓冲区的结构如何,内存访问都将始终对齐)?
答案 0 :(得分:2)
答案 1 :(得分:0)
由于malloc对我们分配缓冲区时要存储的对象类型一无所知,它如何选择返回地址的对齐方式?
malloc(3)
返回"适合任何类型变量的内存。"
它是否只返回一个可被最大对齐整除的地址(在这种情况下,无论我们放入缓冲区的结构如何,内存访问都将始终对齐)?
是的,但请注意您对the strict aliasing rule的遵守情况。
答案 2 :(得分:0)
在最多的情况下,编译器将在该计算机上执行最有益的操作。在大多数平台上,在总线宽度偏移上加载总线宽值是最快的。
这意味着通常在32位计算机上,编译器会选择在4字节偏移上对齐32位数字。在64位计算机上,64位值在8字节偏移上对齐。
在大多数计算机上,较小的值(如8位和16位值)加载较慢。可能会加载它周围的所有4或8个字节,并且需要屏蔽掉所需的字节或两个字节。
当您遇到特殊情况时,可以通过指定对齐方式和填充来覆盖编译器。当你知道快速加载并不重要时,你可能会这样做,但你真的想要紧紧地打包数据。或者当你用铸造和工会玩非常微妙的技巧时。
几乎所有现代计算机上的内存分配例程总是返回至少在平台的总线宽度上对齐的内存(例如4或8字节) - 或者甚至更多 - 如16字节对齐。
当您致电“malloc”时,您有责任了解所需结构的大小。幸运的是,编译器会告诉你任何带有“sizeof”的结构的大小。这意味着如果打包结构以节省内存,sizeof将返回比解压缩结构更小的值。所以你真的会节省内存 - 如果你在大数组中分配小结构。
如果您一次分配一个小型包装结构 - 那么是 - 如果您打包它们,它将没有任何区别。那是因为当你分配一些奇怪的小内存时 - 分配器实际上会使用比这更多的内存。它将为您分配一个方便大小的内存块,然后为自己分配一个额外的内存块来跟踪您的分配。
这就是为什么如果你关心内存使用并希望打包你的结构 - 你绝对不希望一次分配它们。