因此,在大多数实现中,malloc在分配的内存之前存储一个标头,以跟踪分配的内存大小(以便它可以执行free和recalloc)。标题内容是什么?
我写了一个天真的代码来找到它,但它没有任何意义
int * ptr;
ptr = malloc(12*sizeof(int));
printf("Header = %d\n",*(ptr-1));
返回
Header = 57
这里发生了什么?
答案 0 :(得分:14)
我猜你想学习并看看内存是如何分配的。我会忽略未定义的行为答案。当你谈到可移植性等时,它们是正确的(当然),但这不是你的问题。我认为尝试找出分配方式是一个非常好的主意。
首先,我建议您开始查看适用于您平台的malloc实现。如果该代码不可用,那么您运气不佳,您唯一可以做的就是谷歌了解分配是如何完成的。
如果运行linux,可以查看glibc或uclibc的malloc实现。这里是uclibc实现的链接: http://git.uclibc.org/uClibc/tree/libc/stdlib/malloc/malloc.c 代码有很多评论,但可能是压倒性的。
对于您的问题,请查看第104行的http://git.uclibc.org/uClibc/tree/libc/stdlib/malloc/malloc.h。 这是你在谈论的部分。您会看到布局取决于MALLOC_HEADER_SIZE,它可能因不同系统而异。通过阅读代码,您可以了解要使用的类型,以及存储内存大小的偏移量(在此特定实现中)
当然,上面只是uclibc的一个示例实现,可以帮助您入门......
答案 1 :(得分:6)
没人真正回答数字" 57"来自,所以这是我对它的理解。
使用malloc或calloc时设置的标头,至少在我使用的体系结构上,是运行时堆上的内存块的总大小,加上一些"布尔标志&# 34。
您请求了12个整数,每个int(大概)为4个字节。 12x4 = 48.另一个4字节(标题块本身(数字57))被添加到此计数中,使我们保持在52。 那么为什么你会得到57?
好吧,Malloc和Calloc仅以8位块请求内存,以避免总线错误。 8的下一个更高倍数是56。
现在,请记住,任何可被8整除的数字都有一个二进制表示形式,总是以三个0结尾。作为C的内存保存语言,编译器利用这一事实并使用最后三个0作为布尔标志。
在这种特定情况下,设置最后一个布尔标志,加1到56,当读作int时产生数字57。
答案 2 :(得分:4)
这些都不是您的业务(它是实施细节,对用户不透明),您所做的是未定义的行为。
就标准而言。
现在,如果你想要顽皮并且在内存周围徘徊,请注意指针算术以类型大小为单位运行(例如int
为4)。因此,您应始终将指针投射到char*
(或未签名版本)以获取此类恶作剧:
struct Foo * f = malloc(sizeof(Foo) * 7);
const unsigned char * const i_know_what_im_doing = f;
printf("%02X\n", *(i_know_what_im_doing - 1));
答案 3 :(得分:1)
您正在导致未定义的行为 尝试读取超出已分配内存的范围是UB。
我相信你想要找到的是编译器的实现细节,并且因编译器而异。
答案 4 :(得分:1)
这是未定义的行为。但说实话,如果你愿意在malloc的内部进行讨论,那么你需要做的第一件事就是确定你的malloc在做什么。
一旦你确定了这一点,那么你可以做一些更聪明的事情,而不仅仅是随机访问你所分配范围之外的内存(这就是问题)。
答案 5 :(得分:0)
使用你的代码你之前读过一个int,没有任何东西告诉你标题是那么长。 而且,它是特定于实现的,所以你永远不能依赖它!
此外,尝试访问您自己未分配的内存是未定义的行为!!
我不知道你为什么要访问它,但最好不要做任何事情!
如果你想了解更多信息,在大多数现代计算机上,分段和分页的内存,以及你一次可以做的最大分配就是内存段的大小。
因此,您的标头必须至少包含该大小。
但是你可以使用malloc而不是段大小,它们可能是存储在这个标题中的附加信息,你现在永远都不可能!