为什么malloc在gcc中将值初始化为0?

也许它从平台到平台是不同的,但是

当我使用gcc编译并运行下面的代码时,我每次在我的Ubuntu 11.10中都得到0。

#include <stdio.h> #include <stdlib.h> int main() { double *a = (double*) malloc(sizeof(double)*100) printf("%f", *a); } 

为什么即使有calloc,malloc的行为也是如此?

这是否意味着即使您不希望有时将值初始化为0,也会产生不必要的性能开销?


编辑:哦,我以前的例子不是开始,但恰巧使用“新鲜”块。

我正在寻找的是为什么它分配一个大块时初始化它:

 int main() { int *a = (int*) malloc(sizeof(int)*200000); a[10] = 3; printf("%d", *(a+10)); free(a); a = (double*) malloc(sizeof(double)*200000); printf("%d", *(a+10)); } OUTPUT: 3 0 (initialized) 

但是,谢谢指出,当mallocing有一个安全的原因! (从来没有想过)。 当分配新块或大块时,必须将其初始化为零。

简答:

它不,它只是恰巧在你的情况下为零。
(你的testing用例也不会显示数据是零,只有一个元素为零时才显示。


长答案:

当你调用malloc() ,会发生以下两件事情之一:

  1. 它回收以前分配的内存并从相同的进程中释放。
  2. 它从操作系统请求新的页面。

在第一种情况下,内存将包含先前分配剩余的数据。 所以它不会是零。 执行小分配时,这是通常的情况。

在第二种情况下,内存将来自操作系统。 当程序运行内存不足时,或者请求非常大的分配时,会发生这种情况。 (如你的例子中的情况)

这里有一个问题: 出于安全原因,来自操作系统的内存将被清零。

当操作系统给你记忆,它可能已经从一个不同的过程中被释放。 所以内存可能包含密码等敏感信息。 因此,为了防止您读取这些数据,操作系统在将其提供给您之前将其清零。

*我注意到C标准没有提到这一点。 这是严格的操作系统行为。 所以这个调零可能会也可能不会出现在不担心安全的系统上。


为了给更多的performance背景:

作为@R。 在评论中提到,这个调零是为什么你应该总是使用calloc()而不是malloc() + memset()calloc()可以利用这个事实来避免一个单独的memset()


另一方面,这种调零有时是性能瓶颈。 在一些数字应用程序(如不适用的FFT )中,您需要分配大量的暂存内存。 用它来执行任何algorithm,然后释放它。

在这些情况下,调零是不必要的,并且相当于纯粹的开销。

我见过的最极端的例子是在48秒的暂存缓冲区中进行了70秒的操作,清零时间为20秒。 (大约30%的开销) (当然,机器确实缺less内存带宽。)

显而易见的解决scheme是简单地手动重用内存。 但是这往往需要打破已经build立的界面。 (特别是如果它是图书馆例程的一部分)

操作系统通常会清除它发送给您的进程的新内存页面,因此无法查看较旧的进程数据。 这意味着你第一次初始化一个variables(或者malloc的东西)时,它通常是零,但是如果你重复使用了这个内存(比如释放它和malloc),那么所有的赌注都是closures的。

这种不一致就是为什么未初始化的variables是如此难以发现的错误。


至于不想要的性能开销, 避免不明确的行为可能更重要 。 无论在这种情况下可以获得的小的性能提升如果有人稍微修改代码(破坏先前的假设)或将其移植到另一个系统(假设可能已经失效),则不会弥补难以find的错误首先)。

为什么你认为malloc()初始化为零? 这恰好是第一次调用malloc()会调用sbrkmmap系统调用,从OS分配一页内存。 由于安全原因,OS必须提供零初始化内存(否则,来自其他进程的数据变得可见!)。 所以你可能会认为 – 操作系统浪费时间归零页面。 但不是! 在Linux中,有一个特殊的系统范围的单独页面被称为“零页面”,该页面将被映射为“写入时复制”,这意味着只有当您在该页面上实际写入时,操作系统才会分配另一页面初始化它。 所以我希望这能回答你关于performance的问题。 内存分页模型通过支持同一页面的多重映射能力加上处理第一次写入时的情况的能力,允许对内存的使用进行sorting。

如果你调用free()glibc分配器会把这个区域返回到它的空闲列表中,当再次调用malloc()时,你可能会得到同样的区域,但是与之前的数据不一致。 最后, free()可能会通过再次调用系统调用将内存返回给操作系统。

注意, malloc()上的glibc 手册页严格地说内存不被清除,所以通过API上的“契约”,你不能假定它被清除了。 这是原始摘录:

malloc()分配大小字节并返回一个指向分配内存的指针。
内存不被清除。 如果size为0,则malloc()返回NULL,或返回一个唯一的指针值,稍后可以成功传递给free()。

如果您愿意,如果您担心性能或其他副作用,可以阅读有关该文档的更多信息。

我修改你的例子包含2个相同的分配。 现在很容易看到malloc不会初始化内存。

 #include <stdio.h> #include <stdlib.h> int main(void) { { double *a = malloc(sizeof(double)*100); *a = 100; printf("%f\n", *a); free(a); } { double *a = malloc(sizeof(double)*100); printf("%f\n", *a); free(a); } return 0; } 

用gcc输出4.3.4

 100.000000 100.000000 

标准并没有规定malloc()应该初始化为零值。 它只是在你的平台上发生的,它可能被设置为零,或者在你读取该值的特定时刻可能为零。

你的代码没有certificatemalloc初始化它的内存为0.这可以通过操作系统在程序启动之前完成。 为了看到这种情况,给内存写一个不同的值,释放它,并再次调用malloc。 你可能会得到相同的地址,但你必须检查这个。 如果是这样,你可以看看它包含什么。 让我们知道!

来自gnu.org :

非常大的块(比页面大得多)被mmap(匿名或通过/ dev / zero )分配给这个实现。

你知道这是肯定被初始化吗? malloc()返回的区域是否可能在开始时经常有0?

永远不要指望任何编译器生成将内存初始化为任何内容的代码。 malloc只是返回一个指向n个字节的内存的地方,它甚至可能在交换。

如果内存的内容非常重要,请自行初始化。