【问题标题】:calloc -- Usefulness of zeroing out memorycalloc -- 清零内存的用处
【发布时间】:2011-01-16 09:45:30
【问题描述】:

清零内存有什么优势(即calloc() 优于malloc())?反正你不会把值改成别的吗?

【问题讨论】:

    标签: c memory memory-management malloc calloc


    【解决方案1】:

    有两个阵营:一个说在声明变量时初始化变量有助于发现错误。这个阵营中的人确保他们声明的所有内容都已初始化。他们将指向NULLints 的指针初始化为 0,等等。想法是一切都是确定的,当他们在调试器中看到 NULL 指针时,他们立即知道它没有正确设置。它还可以帮助您的程序在测试期间由于 NULL-pointer 取消引用而崩溃,而不是在生产运行中神秘地崩溃。

    另一个阵营说,在声明时初始化变量会使事情更难调试,因为现在编译器无法警告您“未经设置就使用”的变量。

    不告诉你我的个人喜好1:如果你属于第一阵营,你会想要calloc()而不是malloc()。如果你属于第二个阵营(显然属于)那么你更喜欢malloc()而不是calloc()

    现在有两个例外:

    • 如果你属于“初始化一切”阵营,你不是calloc()而是malloc(),因为你正在初始化浮点数或指针,并且你知道所有位为零并不一定意味着@987654331 @为他们。或者,您不想要额外的开销。
    • 如果您属于“需要时设置”阵营,您可能希望在分配一些数据时calloc() 并希望它全为零。例如,如果您想通过动态分配的m 计算n 的逐行总和int 数据。

    1你可以在 SO 上看到我对许多问题的回答,看看我属于哪个阵营:-)。

    【讨论】:

    • 我想让你说出一台 1970 年制造的计算机,它不使用零作为 NULL 指针或将浮点值清零。更不用说,最近几年我编写了 Windows 和 Linux,一个简单的 malloc 将内存归零。我相信它应该是一项安全功能。
    • 所有 IEEE 浮点格式都定义了“所有位为零”表示真零的特殊情况。
    • 更多的是标准保证什么的问题。如果我真的想要NULL 指针,我会在循环中将它们设置为NULL
    • @akillio:不过,这几乎是一个哲学问题:您是按照标准进行编程,还是按照您知道的实现进行编程?就个人而言,我尝试按照标准 if 进行编程,我正在编写所谓的可移植代码:这比了解 1970 年制造的每台计算机要容易。过去,我真的对编译器这样做感到惊讶我认为“永远不会发生”的事情。一个浮现在脑海中的例子是具有中端数据类型 ffs 的 ARM ABI。显然 0 仍然是按位 0,所以不是您要求的示例。
    • @Steve:出于安全原因,brk()sbrk() 系统调用(用于扩展内存分配的 Linux 系统调用)返回归零内存。堆管理器从 sbrk() 请求额外的内存。但是当本地分配释放某些东西时,堆管理器可能不会在后续分配之前将其清除 - 仅设置标头信息来管理它。
    【解决方案2】:
    1. 通过了解已经存在的值,程序员可以采取一些捷径并进行某些优化。最常见的是,calloc使用指针结构:它们被初始化为 NULL。
    2. 如果程序员忘记在分配中初始化某些东西怎么办?零是一个很好的默认值,而不是随机的东西。

    在我很久以前工作的实时过程控制系统中,我们决定让开机逻辑将所有 RAM 初始化为 0xCC,即 8086 的 interrupt 3 指令。如果处理器以某种方式执行未初始化的内存,这将导致处理器进入监视器(原始调试器)。 (不幸的是,8086 会愉快地执行包含零的内存,因为它们是 add [bx+si],al 指令。即使是 32 位模式也会导致它们成为 add [ax],al 指令。)

    我不记得我们是否曾发现过失控程序,但对应于 0xCC 的值有多种:52,428(无符号 16 位)、-19,660(有符号 16 位)、-107374176(32 位浮点数)、并且 -9.25596313493e+61(64 位浮点数)出现在很多意想不到的地方。此外,一些期望字符为 7 位 ASCII 的代码(即一个错误)在尝试处理 0xCC 时提醒我们它的存在。

    【讨论】:

    • calloc 将指针设置为NULL 是没有用的,因为标准不能保证全位为零 == NULL。浮点数也是如此。
    • 自 1970 年代后期以来,情况并非如此。所有重要的架构都使用(void *) 0 作为NULL,而IEEE 浮点格式都使用“所有位为零”作为真零。
    • (void *)0 不一定全为零。编译器必须将(void *)0 转换为适当的“空指针常量”。同样,当您编写p = 0; 并且p 是一个指针时,编译器必须将p 设置为一个等于空指针常量的位模式,这可能不是所有位都为零。
    • 类似的整数类型(unsigned char 和 C99 固定大小类型 (u)intN_t 除外)允许具有填充位,将其设置为 0 可以是陷阱表示(对于远-如果存在反奇偶校验位,则所有位为零是奇偶校验违规)。不太可能,彻头彻尾的邪恶,但如果你按标准生活,那么你可以按标准死……
    • NULL 的二进制表示不依赖于架构 (AFAIK),而是编译器供应商的选择。我过去使用的旧 Watcom C 编译器(在 x86 上)表示带有 0xffffffff 的 NULL 指针。
    【解决方案3】:

    假设您要编写计数排序实现,或者深度优先搜索图并跟踪访问过的顶点。您将在算法运行时更新内存(而不是只分配一次值)。您需要在开始时将其初始化为零。如果您没有calloc,则必须手动检查它并在算法开始时将其初始化为零。 calloc 可能会更有效地为您做到这一点。

    【讨论】:

    • 赞成,因为其余的答案只提到了寻找错误或捷径的优势(这也是很好的信息)。他们没有提到(可能是)calloc的最初预期目的。有时您有一个分配内存的算法,并要求将数据初始化为零。就那么简单。仅仅因为你最终会存储一个不同的值并不意味着零不是正确的初始值。
    • @Merlyn:对于“其余答案”的一些价值 :-)
    • 哦,对不起。没想到你还在说话! ;)
    【解决方案4】:

    很高兴知道您分配的任何内容都被初始化为零。许多错误来自使用未初始化内存的代码。另外,结构/类中的一些默认值可能为零,因此您不需要在 malloc 之后更改所有值。

    例如,分配一个结构,其中包含一些带有 malloc 的指针。除非将它们设置为 NULL,否则 NULL 检查并不总是有效。如果你 calloc,你不必为指针值做额外的初始化步骤。

    【讨论】:

    • 正如我在另一条评论中所说:calloc 将指针设置为 NULL 是没用的,因为标准不能保证全位为零 == NULL
    【解决方案5】:

    除了初始化变量的好处之外,calloc 还有助于跟踪错误。

    如果您在未正确初始化的情况下意外使用了一些分配的内存,应用程序将始终以同样的方式失败。例如,来自空指针的访问冲突。使用 malloc 内存具有随机值,这可能导致程序以随机​​方式失败。

    随机故障很难追踪,而 calloc 有助于避免这些。

    【讨论】:

      【解决方案6】:

      首先你不能调用指针,至少如果你想遵循标准 C 的话。

      其次,当你用全零破坏成员时,错误就会被掩盖。最好有一个调试版本的 malloc,它将内存初始化为总是会崩溃的东西,例如 0xCDCDCDCD。

      然后,当您看到 Access voilation 时,您会立即知道问题所在。 拥有 debug free 功能也是有益的,它会以不同的模式鞭打内存,这样那些在内存被释放后触摸内存的人会得到意想不到的惊喜。

      在嵌入式系统上工作,调用只是为了“确定”通常不是一种选择。您通常一次性分配和填充,因此 calloc 只是让您双重记忆。

      【讨论】:

        【解决方案7】:

        没有人谈到性能方面,所以我想我必须这样做。如果您需要使用“以防万一”集成 memset 编写一个非常快速的程序 malloc 不是一个好方法。不管 memset 有多快,它总是太慢。有时你必须初始化一个向量或一个数组,所以真正的问题是控制你的时钟周期(即不浪费它们)。我曾经听过一句话“你永远不应该意外放弃性能”,这意味着从性能的角度来看,你必须始终知道为什么选择以一种或另一种方式实现代码(优缺点是什么以及如何权衡它们在特定情况下相互)。

        如果您有一个将用字符串填充的缓冲区,那么在填充字符串之前对其进行初始化可能是“不错的”,但大多数人会同意这完全浪费了时钟周期。如果您正在编写一个新的 str* 函数,您可能希望 - 出于调试目的 - 用一个通常不应该出现的值填充缓冲区,但在分发时这将被删除。

        正如其他人所提到的,如果正在访问未初始化的变量,编译器会发出警告,因此我认为最重要的是,“以防万一”初始化确实没有任何借口。

        【讨论】:

          猜你喜欢
          • 2020-07-28
          • 2010-12-09
          • 2021-11-20
          • 2021-01-08
          • 2015-08-21
          • 1970-01-01
          • 1970-01-01
          • 2010-12-02
          • 1970-01-01
          相关资源
          最近更新 更多