我曾尝试在具有不同处理器和主内存大小的某些不同计算机上运行此代码。
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
// your code goes here
int *a, i;
a = (int *)malloc(10*(sizeof(int)));
for(i = 0; i < 10; i++)
{
a[i] = i*i;
}
free(a);
a = (int *)malloc(10*(sizeof(int)));
for(i = 0; i < 10; i++)
{
printf("%d\n",a[i]);
}
free(a);
return 0;
}
但是,在所有机器中它都会生成相同的输出:
0
1
4
9
16
25
36
49
64
81
我的问题是:变量'a'总是分配使用相同的内存位置来执行吗?
理论上,它可能不是强制性的。如果我的理解是正确的:'a'的每个malloc都可以分配不同的基地址。
然而,现代计算机器在实践中实际发生了什么?
答案 0 :(得分:4)
现代内存分配器有几个性能优化。它们有几个可用内存桶,每次请求它们的字节数并确定可用的最佳内存区域。一些内存分配器保留一个指向你最后一次分配的指针,所以如果你需要另一个相同大小的东西(经常发生的事情),它们会给你同样的一块,这样他们就不必寻找一个新的,需要时间。
您可以尝试两次拨打malloc()
,然后拨打free()
再拨打malloc()
,看看会发生什么。
答案 1 :(得分:3)
无法保证这将起作用。 (事实上,它不适用于Visual Studio中的调试版本,因为调试内存分配器会在释放数据时破坏数据,以便检测此类错误。)
你永远不应该依赖这种行为。
某些内存管理器使用释放的内存块的LIFO(后进先出)列表。这意味着相同大小的下一次分配可能会获得相同的块。其他内存管理器显然不使用LIFO空闲列表来使恶意软件更难利用堆管理错误。
答案 2 :(得分:3)
它在实践中发生,因为重新使用刚刚释放的内存块通常是最佳选择。它可能仍然在缓存中很热,这意味着可以缩短malloc的自由列表(而不是将该块保留在空闲列表中并从操作系统获取新块)。
这也意味着malloc
可能不需要任何系统调用来满足此请求。小free()
次呼叫通常不会立即返回到操作系统(因为通常它们不会,因为它们只是页面的一部分)。因此,典型的malloc实现将它们放在一个空闲列表中。
所以这个小实验告诉你一些关于你尝试过的特定C实现的malloc的内部实现。正如我认为你从问题的措辞中认识到的那样,这是未定义的行为,永远不应该依赖。
它并不是特定于CPU架构。 AFAIK,GNU libc&#39; malloc
在每个架构上使用相同的算法。
即使在以这种方式工作的C实现上,也有很多方法可以解决这个问题:信号处理程序可以在free和第二个malloc之间运行,并获取块。在多线程代码中,另一个线程可以在free和malloc之间使用该块。 (虽然这不太可能:每个线程通常会使用自己的小池)。
通常的大缓冲区将传递回free()
调用内的操作系统,因此下一个malloc()
必须再次从操作系统获取新内存。有关glibc的malloc中可调参数的说明,请参阅mallopt(3)
中的M_MMAP_THRESHOLD
。 M_PERTURB
提供了与Adrian为MSVC ++调试版本描述的功能类似的功能:破坏具有释放后使用错误的代码,并破坏依赖于malloc
内存被归零的代码(使用{{1有效地获取归零的内存)。