我遇到了以下代码:
int main()
{
char *A=(char *)malloc(20);
char *B=(char *)malloc(10);
char *C=(char *)malloc(10);
printf("\n%d",A);
printf("\t%d",B);
printf("\t%d\n",C);
return 0;
}
//output-- 152928264 152928288 152928304
我想知道
malloc()
是如何进行分配和填充的。查看输出,可以看到起始地址是 8 的倍数。还有其他规则吗?
根据此文档页面,
GNU 系统中 malloc 或 realloc 返回的块地址始终是 8 的倍数(在 64 位系统上是 16)。
一般来说,
malloc
的实现是特定于系统的。它们都为自己的簿记保留了一些内存(例如分配块的实际长度),以便在您调用 free
时能够正确释放该内存。如果需要对齐到特定边界,请使用其他函数,例如 posix_memalign
。
C 标准规定
malloc()
的结果必须能够转换为任何合法的指针类型。所以
... = (DataType *)malloc(...);
一定是可能的,无论
DataType
是什么类型。
如果系统对某些数据类型有内存对齐要求,则
malloc()
必须考虑到这一点。由于 malloc()
无法知道要将结果转换为哪种指针类型,因此它始终必须遵循最严格的内存对齐要求。
标准中原来的写法是:
如果分配成功,则返回的指针会被适当对齐,以便可以将其分配给指向具有基本对齐要求的任何类型对象的指针,然后用于访问分配的空间中的此类对象或此类对象的数组(直到空间被显式释放)。
来源:ISO/IEC 9899:201x(又名 ISO C11)
例如如果系统要求
int
为 4 字节对齐且 long
为 8 字节对齐,则 malloc()
必须返回 8 字节对齐的内存,因为它无法知道您是否要将结果转换为 int *
或 long *
。
理论上,如果您请求的字节数少于
sizeof(long)
,则转换为 long *
是无效的,因为 long
甚至不适合该内存。人们可能会认为在这种情况下 malloc()
可以选择较小的对齐方式,但这不是标准所说的。标准中的对齐要求并不取决于分配的大小!
由于许多 CPU 以及许多操作系统确实有对齐要求,因此大多数 malloc 实现将始终返回对齐的内存,但它遵循的对齐规则是特定于系统的。还有一些 CPU 和系统没有对齐要求,在这种情况下
malloc()
也可能返回未对齐的内存。
如果您依赖于特定的对齐方式,则可以使用
aligned_alloc()
,它是在 ISO C11 标准中定义的,因此可移植到存在 C11 编译器的所有系统,或者您可以使用 posix_memalign()
,它是定义的符合 IEEE Std 1003.1-2001(又名 POSIX 2001),可用于所有符合 POSIX 的系统以及尽可能符合 POSIX 的系统(例如 Linux)。
有趣的事实:
macOS 上的
malloc()
始终返回 16 字节对齐的内存,尽管 macOS 上没有数据类型的内存对齐要求超过 8。原因是 SSE。一些 SSE 指令有 16 字节对齐要求,通过确保 malloc()
始终返回 16 字节对齐的内存,Apple 可以经常在其标准库中使用 SSE 优化。
唯一的标准规则是
malloc
返回的地址将适当对齐以存储任何类型的变量。这到底意味着什么是特定于平台的(因为对齐要求因平台而异)。
对于 32 位 Linux 系统:
当malloc()分配内存时,它以8的倍数分配内存(8的填充)并分配额外的8字节用于簿记。
例如:
malloc(10) 和 malloc(12) 将分配 24 字节内存(填充后 16 字节 + 8 用于记账的字节)。
malloc() 进行填充,因为返回的地址将是八的倍数,因此对任何类型的指针都有效。当我们调用 free 函数时,使用簿记 8 个字节。簿记字节存储分配的内存的长度。