>以下摘自此处
pw = (widget *)malloc(sizeof(widget));
分配原始存储。实际上,malloc 调用分配了存储 足够大且适当对齐以容纳类型的对象 控件
还看到Herb sutter的Fast pImpl,他说:
对齐。任何内存对齐。分配的任何内存 动态通过新的或 malloc 保证正确对齐 任何类型的对象,但未动态分配的缓冲区 没有这样的保证
我对此很好奇,malloc 如何知道自定义类型的对齐方式?
要求是递归的:任何struct
的对齐方式只是其任何成员的最大对齐方式,这是递归理解的。
例如,假设每个基本类型的对齐方式等于其大小(通常并不总是如此(,struct X { int; char; double; }
具有 double
的对齐方式,并且它将被填充为双精度大小的倍数(例如 4 (int(、1 (char(、3 (填充(、8 (double((。struct Y { int; X; float; }
的对齐方式为X
,最大且等于double
的对齐方式,Y
的布局相应为:4(整数(,4(填充(,16(X(,4(浮点(,4(填充(。
(所有数字都只是示例,在您的计算机上可能会有所不同。
因此,通过将其分解为基本类型,我们只需要知道少数基本对齐,其中有一个众所周知的最大对齐方式。C++甚至定义了一个类型max_align_t
其对齐方式是最大的对齐方式。
malloc()
需要做的就是选择一个是该值的倍数的地址。
我认为Herb Sutter引用中最相关的部分是我用粗体标记的部分:
对准。任何内存对齐。通过 new 或 malloc 动态分配的任何内存都保证与任何类型的对象正确对齐,但未动态分配的缓冲区没有这样的保证
它不必知道你想到什么类型,因为它与任何类型对齐。在任何给定的系统上,都有一个必要或有意义的最大对齐大小;例如,具有四字节字的系统可能最多具有四字节对齐方式。
malloc(3)
手册页也清楚地表明了这一点,其中部分内容是:
malloc()
和calloc()
函数返回一个指向已分配内存的指针,该指针与任何类型的变量适当对齐。
malloc()
可以使用的唯一信息是传递给它的请求的大小。通常,它可能会执行诸如将传递的大小四舍五入到最接近的更大(或等于(的 2 的幂之类的操作,并根据该值对齐内存。对齐值也可能有一个上限,例如 8 个字节。
以上是一个假设的讨论,实际实现取决于您使用的机器体系结构和运行时库。也许您的malloc()
总是返回在 8 个字节上对齐的块,并且它永远不必做任何不同的事情。
1( 对齐到所有对齐方式中最小公的倍数。 例如,如果整数需要 4 字节对齐,但指针需要 8,则将所有内容分配给 8 字节对齐。这会导致所有内容对齐。
2( 使用 size 参数确定正确的对齐方式。对于较小的大小,您可以推断类型,例如malloc(1)
(假设其他类型大小不是 1(始终是字符。 C++ new
具有类型安全的优点,因此始终可以通过这种方式做出对齐决策。
在 C++11 之前,通过使用最大对齐方式来处理相当简单的对齐,其中确切值未知,malloc/calloc 仍然以这种方式工作。这意味着 malloc 分配可以正确对齐任何类型。
根据标准,错误的对齐可能会导致未定义的行为,但我看到 x86 编译器很慷慨,只会惩罚较低的性能。
请注意,您还可以通过编译器选项或指令调整对齐方式。(例如VisualStudio的编译指示包(。
但是当涉及到放置新位置时,C++11 为我们带来了新的关键字,称为 alignof 和 alignas。下面是一些代码,显示了编译器最大对齐方式大于 1 时的效果。下面第一个新展示位置自动良好,但第二个位置不是。
#include <iostream>
#include <malloc.h>
using namespace std;
int main()
{
struct A { char c; };
struct B { int i; char c; };
unsigned char * buffer = (unsigned char *)malloc(1000000);
long mp = (long)buffer;
// First placment new
long alignofA = alignof(A) - 1;
cout << "alignment of A: " << std::hex << (alignofA + 1) << endl;
cout << "placement address before alignment: " << std::hex << mp << endl;
if (mp&alignofA)
{
mp |= alignofA;
++mp;
}
cout << "placement address after alignment : " << std::hex <<mp << endl;
A * a = new((unsigned char *)mp)A;
mp += sizeof(A);
// Second placment new
long alignofB = alignof(B) - 1;
cout << "alignment of B: " << std::hex << (alignofB + 1) << endl;
cout << "placement address before alignment: " << std::hex << mp << endl;
if (mp&alignofB)
{
mp |= alignofB;
++mp;
}
cout << "placement address after alignment : " << std::hex << mp << endl;
B * b = new((unsigned char *)mp)B;
mp += sizeof(B);
}
我想这段代码的性能可以通过一些按位运算来提高。
编辑:用按位运算取代了昂贵的模计算。仍然希望有人能更快地找到一些东西。
malloc 不知道它分配了什么,因为它的参数只是总大小。它只是对齐对任何对象都安全的对齐方式。
你可能会找到这个小 C 程序的 malloc(( 实现的分配位:
#include <stdlib.h>
#include <stdio.h>
int main()
{
size_t
find = 0,
size;
for( unsigned i = 1000000; i--; )
if( size = rand() & 127 )
find |= (size_t)malloc( size );
char bits = 0;
for( ; !(find & 1); find >>= 1, ++bits );
printf( "%d", (int)bits );
}