为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

一只甜甜圈 2019-07-20 10:45:42
为什么malloc在GCC中将值初始化为0?也许不同的平台是不同的,但是当我使用GCC编译并运行下面的代码时,我每次在ubuntu11.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)但是,谢谢你指出,有一个安全的原因时,错位!(从未想过)。确保在分配新块或大块时必须将其初始化为零。
查看完整描述

3 回答

?
Cats萌萌

TA贡献1805条经验 获得超9个赞

操作系统通常会清除它发送给进程的新内存页,这样它就不能查看旧进程的数据。这意味着当您第一次初始化变量(或malloc之类的东西)时,它通常是零,但是如果您曾经重用该内存(例如,通过释放它和再次释放malloc-ing),那么所有的赌注都会停止。

这种不一致正是为什么未初始化的变量很难找到bug的原因。


至于不必要的表演间接费用,避免未指明的行为可能更重要。..无论在这种情况下你能获得什么小小的性能提升,如果有人稍微修改了代码(打破了以前的假设)或将其移植到另一个系统(假设最初可能是无效的),那么很难找到您必须处理的bug。


查看完整回答
反对 回复 2019-07-20
?
湖上湖

TA贡献2003条经验 获得超2个赞

你为什么认为malloc()初始化为零?恰巧第一次打电话到malloc()结果打电话给sbrkmmap系统调用,它从操作系统分配一页内存。由于安全原因,操作系统必须提供零初始化内存(否则,来自其他进程的数据就会可见!)所以你可能会认为-操作系统浪费时间对页面进行归零。但是没有!在linux中,有一个特殊的系统范围内的单例页面,名为“零页”,该页面将被映射为“复制即写”,这意味着只有当您实际在该页上写入时,操作系统才会分配另一页并初始化它。所以我希望这能回答你关于表现的问题。内存分页模型允许对内存的使用进行排序-通过支持同一页的多个映射的能力,加上在第一次写入发生时处理这种情况的能力。

如果你打电话free()glibc分配器将该区域返回到它的空闲列表,并且当malloc()再次调用时,您可能会得到相同的区域,但与前面的数据不兼容。最终,free()可能通过再次调用系统调用将内存返回到操作系统。

注意,glibc 手册页在……上面malloc()严格地说内存没有被清除,所以根据API上的“约定”,您不能假设它确实被清除了。这是最初的节选:

malloc()分配大小字节并返回指向分配内存的指针。
内存没有清除。如果大小为0,那么malloc()将返回NULL,或者返回一个以后可以成功传递给Free()的唯一指针值。

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


查看完整回答
反对 回复 2019-07-20
?
莫回无

TA贡献1865条经验 获得超7个赞

简短答覆:

没有,只是碰巧在你的情况下是零。
(此外,您的测试用例没有显示数据为零。它只显示一个元素为零的情况。)


详细答覆:

当你打电话malloc()会发生两件事之一:

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

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

在第二种情况下,内存将来自操作系统。当程序内存耗尽时,或者当您请求一个非常大的分配时,就会发生这种情况。(与您的示例中的情况一样)

以下是问题所在:来自操作系统的内存将被归零保安理由*

当操作系统给您内存时,它可以从不同的进程中释放出来。以便内存可以包含敏感信息,如密码。因此,为了防止您读取这些数据,操作系统将在将其提供给您之前将其归零。

*我注意到C标准对此没有任何规定。这是严格意义上的OS行为。因此,这种零化可能存在,也可能不存在于安全性不受关注的系统上。


为了给这方面提供更多的性能背景:

正如@R.在注释中提到的,这个零化是为什么您应该始终使用calloc()而不是malloc() + memset()calloc()可以利用这一事实来避免单独的memset().


另一方面,这种归零有时是性能瓶颈。在一些数值应用中(例如格格不入的FFT),您需要分配一大块划痕内存。使用它来执行任何算法,然后释放它。

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

我所见过的最极端的例子是20秒的零开销,这是一个70秒操作的零开销,它有一个48 GB的划痕缓冲区。(大约30%的间接费用。)(诚然:这台机器确实缺乏内存带宽。)

显而易见的解决方案是简单地手动重用内存。但这往往需要突破已建立的接口。(特别是如果它是库例程的一部分)


查看完整回答
反对 回复 2019-07-20
  • 3 回答
  • 0 关注
  • 1185 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信