以下内容摘自此处
pw = (widget *)malloc(sizeof(widget));
调用malloc函数会分配足够大且适当对齐来容纳widget类型对象的原始存储空间。
此外,可以参考herb sutter的fast pImpl文章。他指出:内存对齐。通过new或malloc动态分配的任何内存都保证适合任何类型的对象,但未动态分配的缓冲区不能得到此种保证。
我很好奇,malloc如何知道自定义类型的对齐方式?
以下内容摘自此处
pw = (widget *)malloc(sizeof(widget));
调用malloc函数会分配足够大且适当对齐来容纳widget类型对象的原始存储空间。
此外,可以参考herb sutter的fast pImpl文章。他指出:内存对齐。通过new或malloc动态分配的任何内存都保证适合任何类型的对象,但未动态分配的缓冲区不能得到此种保证。
我很好奇,malloc如何知道自定义类型的对齐方式?
对齐要求是递归的: 任何struct
的对齐方式仅为其成员中最大的对齐方式,并且这可以递归地理解。
例如,假设每个基本类型的对齐方式等于其大小(通常情况下并非如此),则struct X { int; char; double; }
的对齐方式为double
,并会填充为double大小的倍数(例如:4 (int), 1 (char), 3 (padding), 8 (double))。 struct Y { int; X; float; }
具有X
的对齐方式,其最大值与double
的对齐方式相等,因此Y
的布局将根据其确定:4 (int), 4 (padding), 16 (X), 4 (float), 4 (padding)。
(所有数字仅为示例,可能在您的计算机上不同。)
因此,通过将其分解为基本类型,我们只需要了解少量基本对齐方式,其中有一个众所周知的最大对齐方式。 C++甚至定义了一种类型max_align_t
,其对齐方式即为最大对齐方式。
所有malloc()
需要做的就是选择一个是该值的倍数的地址。
alignas
指定一个自定义对齐方式,其大小大于原始数据类型的最大对齐方式时,会发生什么? - Curiousnew
只有在分配char
或unsigned char
时才保证具有“任何”对齐方式。对于其他类型,它可能具有较小的对齐方式。 - Mooing Ducknew char[16]
以一种被认为保证适合任何类型X
的对齐方式指定,其中sizeof(X)<=16
。 - ruakhchar
和unsigned char
类型,但不适用于signed char
类型。C++规范将char
和unsigned char
视为“字节”类型,但不认为signed char
是“字节”类型。(暗示地,规范实际上并没有直接说“字节类型”这样的话)。 - Mooing Duck malloc()
只能使用传递给它的请求的大小信息。通常,它可能会将传递的大小四舍五入到最接近大于(或等于)二次幂的值,并根据该值对内存进行对齐。可能还会有对齐值的上限,例如8个字节。malloc()
始终返回按8个字节对齐的块,而且它从不执行任何不同的操作。malloc
使用“最坏情况”对齐方式是因为它不知道更好的方法。这是否意味着calloc
可以更聪明,因为它接受两个参数,对象的数量和单个对象的大小? - Aaron McDaid1) 对齐到所有对齐方式的最小公倍数。例如,如果整数需要4字节对齐,但指针需要8字节对齐,则将所有内容分配到8字节对齐。这会导致所有内容都被对齐。
2) 使用大小参数确定正确的对齐方式。对于小尺寸,您可以推断出类型,例如malloc(1)
(假设其他类型大小不为1)始终是一个char。C++的new
有类型安全的好处,因此始终可以通过这种方式做出对齐决策。
在C++11之前,对齐方式通常使用未知确切值的最大对齐方式进行处理,并且malloc/calloc仍然以这种方式工作。这意味着malloc为任何类型正确对齐。
根据标准,错误的对齐可能导致未定义行为,但我看到x86编译器很慷慨,只会降低性能。
请注意,您还可以通过编译器选项或指令来微调对齐方式(例如VisualStudio中的pragma pack)。
但是,当涉及到定位new时,C++11带来了新关键字alignof和alignas。以下是一些代码,如果编译器的最大对齐方式大于1,则显示其效果。下面的第一个定位new自动完成对齐,但第二个不行。
#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不知道它分配的内容是什么,因为它的参数只是总大小。它只会对齐到任何对象都安全的对齐方式。
#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 );
}