jin*_*nge 5 c++ memory-alignment dynamic-memory-allocation
我写了一个简单的例子:
#include <iostream>
int main() {
void* byte1 = ::operator new(1);
void* byte2 = ::operator new(1);
void* byte3 = malloc(1);
std::cout << "byte1: " << byte1 << std::endl;
std::cout << "byte2: " << byte2 << std::endl;
std::cout << "byte3: " << byte3 << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
运行示例,我得到以下结果:
字节1:0x1f53e70
字节2:0x1f53e90
字节3:0x1f53eb0
每次我分配一个内存字节时,它总是16个字节对齐。为什么会这样?
我在GCC 5.4.0和GCC 7.4.0上测试了此代码,并得到了相同的结果。
为什么会发生这种情况?
因为标准是这么说的。更具体地说,它表示动态分配1至少与最大基本2对齐(它可能具有更严格的对齐)对齐。有一个预先定义的宏(因为C ++ 17),只是为了告诉你这保证定位到底是什么目的:__STDCPP_DEFAULT_NEW_ALIGNMENT__。为什么在您的示例中这可能是 16 ......这是语言实现的选择,受目标硬件架构允许的限制。
这是(曾经)一种必要的设计,考虑到(曾经)没有办法将有关所需对齐的信息传递给分配函数(直到 C++17 引入了对齐新语法以分配“过度对齐” “ 记忆)。
malloc对您打算在内存中创建的对象类型一无所知。有人可能认为new理论上可以推断出对齐方式,因为它被赋予了一种类型……但是如果您想为其他具有更严格对齐的对象重用该内存std::vector怎么办,例如在? 一旦您了解了运算符 new: 的 API void* operator new ( std::size_t count ),您就会发现类型或其对齐方式不是可能影响分配对齐方式的参数。
1由默认分配器或malloc函数族生成。
2最大基本对齐是alignof(std::max_align_t)。没有比这更严格的基本类型(算术类型、指针)。
其实有两个原因。第一个原因是,某些类型的对象有一些对齐要求。通常,这些对齐要求是软性的:未对齐的访问“只是”变慢(可能慢几个数量级)。它们也可能很困难:例如,在 PPC 上,如果向量未与 16 字节对齐,则您根本无法访问内存中的向量。对齐不是可选的,而是分配内存时必须考虑的事情。总是。
请注意,无法指定 的对齐方式malloc()。根本没有理由支持这一点。因此,malloc()必须实现提供一个为平台上的任何目的正确对齐的指针。C++ 中的::operator new()遵循同样的原则。
需要多少对齐完全取决于平台。在 PPC 上,您无法避免少于 16 字节的对齐。X86 在这方面稍微宽松一点,据我所知。
第二个原因是分配器函数的内部工作原理。典型的实现具有至少 2 个指针的分配器开销:每当您从中请求一个字节时,malloc()通常需要为至少两个额外的指针分配空间来进行自己的簿记(确切的数量取决于实现)。在 64 位架构上,这是 16 个字节。因此,以malloc()字节为单位思考是不明智的,以 16 字节块来思考更有效。至少。您可以通过示例代码看到这一点:生成的指针实际上相距 32 个字节。每个内存块占用 16 字节有效负载 + 16 字节内部簿记内存。
由于分配器从内核请求整个内存页(4096 字节,4096 字节对齐!),因此生成的内存块在 64 位平台上自然是 16 字节对齐。提供不太对齐的内存分配根本不切实际。
因此,综合考虑这两个原因,从分配器函数提供严格对齐的内存块既实用又必需。确切的对齐量取决于平台,但通常不会小于两个指针的大小。
| 归档时间: |
|
| 查看次数: |
111 次 |
| 最近记录: |