malloc 对内存对齐有哪些保证?

问题描述 投票:0回答:4

我遇到了以下代码:

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 的倍数。还有其他规则吗?

c memory-management malloc
4个回答
22
投票

根据此文档页面

GNU 系统中 malloc 或 realloc 返回的块地址始终是 8 的倍数(在 64 位系统上是 16)。

一般来说,

malloc
的实现是特定于系统的。它们都为自己的簿记保留了一些内存(例如分配块的实际长度),以便在您调用
free
时能够正确释放该内存。如果需要对齐到特定边界,请使用其他函数,例如
posix_memalign


10
投票

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 优化。


7
投票

唯一的标准规则是

malloc
返回的地址将适当对齐以存储任何类型的变量。这到底意味着什么是特定于平台的(因为对齐要求因平台而异)。


0
投票

对于 32 位 Linux 系统:
当malloc()分配内存时,它以8的倍数分配内存(8的填充)并分配额外的8字节用于簿记。

例如:

malloc(10) 和 malloc(12) 将分配 24 字节内存(填充后 16 字节 + 8 用于记账的字节)。

malloc() 进行填充,因为返回的地址将是八的倍数,因此对任何类型的指针都有效。当我们调用 free 函数时,使用簿记 8 个字节。簿记字节存储分配的内存的长度。

© www.soinside.com 2019 - 2024. All rights reserved.