将内存归零(即calloc()
over malloc()
)有什么好处?无论如何,您不会将值更改为其他值吗?
7 回答
有两个阵营:一个说在声明变量时初始化变量有助于发现错误。这个阵营中的人确保他们声明的所有内容都已初始化。他们将指向NULL
、int
s 的指针初始化为 0 等。想法是一切都是确定的,当他们NULL
在调试器中看到 -pointer 时,他们立即知道它没有正确设置。它还可以帮助您的程序在测试期间由于NULL
-pointer 取消引用而崩溃,而不是在生产运行中神秘地崩溃。
另一个阵营说,在声明时初始化变量会使事情更难调试,因为现在编译器无法警告您“未经设置就使用”的变量。
不告诉你我的个人偏好1:如果你属于第一阵营,你会想要calloc()
而不是malloc()
。如果您属于第二个阵营(显然您属于) ,那么您更malloc()
喜欢calloc()
.
现在有两个例外:
- 如果您属于“初始化一切”阵营,那么您不会这样做,
calloc()
而是malloc()
因为您正在初始化浮点数或指针,并且您知道所有位为零并不一定意味着0
它们。或者,您不想要额外的开销。 - 如果您属于“需要时设置”阵营,您可能希望
calloc()
在分配一些数据时希望它全为零。n
例如,如果您想通过m
动态分配的int
数据计算 an 的逐行总和。
1您可以在 SO 上查看我对许多问题的回答,以了解我属于哪个阵营 :-)。
- 通过了解已经存在的值,程序员可以采取一些捷径并进行某些优化。最常见的是,
calloc
使用指针构造一个结构:它们被初始化为 NULL。 - 如果程序员忘记在分配中初始化某些东西怎么办?零是一个很好的默认值,而不是随机的东西。
在我很久以前工作的实时过程控制系统中,我们决定让开机逻辑将所有 RAM 初始化为 0xCC,即 8086 的interrupt 3
指令。如果处理器以某种方式执行未初始化的内存,这将导致处理器进入监视器(原始调试器)。(不幸的是,8086 愉快地执行包含零的内存,因为它们是add [bx+si],al
指令。即使是 32 位模式也会导致它们成为add [ax],al
指令。)
我不记得我们是否曾发现过失控程序,但对应于 0xCC 的值有多种:52,428(无符号 16 位)、-19,660(有符号 16 位)、-107374176(32 位浮点数)和 -9.25596313493 e+61 (64-bit float) 出现在很多意想不到的地方。此外,一些期望字符为 7 位 ASCII 的代码(即一个错误)在尝试处理 0xCC 时提醒我们它的存在。
假设您要编写计数排序实现,或者深度优先搜索图并跟踪访问的顶点。您将在算法运行时更新内存(而不是只分配一次值)。您需要在开始时将其初始化为零。如果您没有calloc
,则必须手动完成它并在算法开始时将其初始化为零。calloc
可以为您更有效地做到这一点。
很高兴知道您分配的任何内容都被初始化为零。许多错误来自使用未初始化内存的代码。另外,结构/类中的一些默认值可能为零,因此您不需要在 malloc 之后更改所有值。
例如,分配一个结构,其中包含一些带有 malloc 的指针。除非将它们设置为 NULL,否则 NULL 检查并不总是有效。如果您使用 calloc,则不必为指针值执行额外的初始化步骤。
除了初始化变量的好处之外,calloc 还有助于跟踪错误。
如果您不小心使用了一些分配的内存而没有正确初始化它,应用程序将始终以同样的方式失败。例如,来自空指针的访问冲突。使用 malloc 内存具有随机值,这可能导致程序以随机方式失败。
随机故障很难追踪,而 calloc 有助于避免这些故障。
首先,你不能调用指针,至少如果你想遵循标准 C 的话。
其次,当你用全零破坏成员时,错误就会被掩盖。最好有一个 malloc 的调试版本,它将内存初始化为总是会崩溃的东西,例如 0xCDCDCDCD。
然后,当您看到 Access voilation 时,您会立即知道问题所在。具有调试免费功能也很有好处,该功能将以不同的模式鞭打内存,因此那些在内存被释放后触摸内存的人会得到意想不到的惊喜。
在嵌入式系统上工作,调用只是为了“确定”通常不是一种选择。您通常一次性分配和填充,因此 calloc 只是男性您是双重记忆。
没有人谈到性能方面,所以我想我必须这样做。如果您需要使用“以防万一”集成 memset 编写一个非常快速的程序 malloc 不是一个好方法。不管 memset 有多快,它总是太慢。有时您必须初始化一个向量或一个数组,以便真正的问题是控制您的时钟周期(即不浪费它们)。我曾经听过一句话“你永远不应该意外放弃性能”,这意味着从性能的角度来看,你必须始终知道为什么选择以一种或另一种方式实现代码(优缺点是什么以及如何权衡它们在特定情况下相互)。
如果您有一个将用字符串填充的缓冲区,那么在填充字符串之前对其进行初始化可能是“很好的”,但大多数人会同意这完全浪费了时钟周期。如果您正在编写一个新的 str* 函数,您可能希望 - 出于调试目的 - 用一个通常不应该出现的值填充缓冲区,但这将在分发时被删除。
正如其他人提到的那样,如果正在访问未初始化的变量,编译器会发出警告,所以我认为最重要的是,“以防万一”初始化确实没有任何借口。