归零记忆

gcc 4.4.4 C89

我只是想知道大多数C程序员在想要将内存清零时做了什么。

例如,我有一个1024字节的缓冲区。 有时我会这样做:

char buffer[1024] = {0}; 

这将使所有字节为零。

但是,我应该这样声明并使用memset吗?

 char buffer[1024]; . . memset(buffer, 0, sizeof(buffer)); 

有没有真正的理由你必须将记忆归零? 没有做到最糟糕的是什么?

可能发生的最坏情况? 您最终(无意中)使用非NULL终止的字符串,或者在打印到缓冲区的一部分后inheritance其右侧的任何内容的整数。 然而,即使您初始化缓冲区,未终止的字符串也可能以其他方式发生。

编辑 (来自评论)世界末日也是一个遥远的可能性,取决于你在做什么。

两者都是不受欢迎的。 但是,除非完全避开动态分配的内存,否则大多数静态分配的缓冲区通常都很小,这使得memset()相对便宜。 实际上,比大多数调用calloc()的动态块要便宜得多,动态块往往大于~2k。

c99包含有关默认初始化值的语言,但我似乎无法使用任何类型的存储使gcc -std=c99同意。

尽管如此,由于许多较旧的编译器(以及不完全是c99的编译器)仍在使用中,我更喜欢使用memset()

我非常喜欢

 char buffer[1024] = { 0 }; 

它更短,更易于阅读,并且不易出错。 仅在动态分配的缓冲区上使用memset ,然后更喜欢calloc

在没有初始化的情况下定义char buffer[1024] ,您将获得未定义的数据。 例如,调试模式下的Visual C ++将使用0xcd初始化。 在发布模式下,它将简单地分配内存,而不关心以前使用该块中发生的情况。

此外,您的示例演示了运行时与编译时初始化。 如果char buffer[1024] = { 0 }是全局或静态声明,它将使用其初始化数据存储在二进制数据段中,从而将二进制大小增加大约1024个字节(在本例中)。 如果定义在函数中,则它存储在堆栈中并在运行时分配,而不是存储在二进制文件中。 如果在这种情况下提供初始化程序,则初始化程序存储在二进制文件中,并且等效的memcpy()用于在运行时初始化buffer

希望这有助于您确定哪种方法最适合您。

在这种特殊情况下,没有太大区别。 我更喜欢= { 0 }不是memset因为memset更容易出错:

  • 它提供了一个错误界限的机会。
  • 它提供了将参数混合到memset的机会(例如memset(buf, sizeof buf, 0)而不是memset(buf, 0, sizeof buf)

通常, = { 0 }也更适合初始化struct 。 它有效地初始化所有成员,就像你写了= 0来初始化每个成员一样。 这意味着保证指针成员被初始化为空指针( 可能不是全位为零 ,如果使用了memset ,则所有位为零)。

另一方面, = { 0 }可以将struct填充位struct垃圾,因此如果您打算稍后使用memcmp进行比较,则可能不合适。

不执行此操作可能发生的最糟糕情况是您逐个字符地写入一些数据,然后将其解释为字符串(并且您没有编写空终止符)。 或者你最终未能意识到它的一部分是未初始化的,并将其视为有效数据。 基本上:各种各样的肮脏。

Memset应该没问题(如果你纠正错字的大小:-))。 我更喜欢你的第一个例子,因为我觉得它更清楚。

对于动态分配的内存,我使用calloc而不是malloc和memset。

我更喜欢使用memset来清除一块内存,尤其是在使用字符串时。 我想毫无疑问地知道在我的字符串之后会有一个空分隔符。 是的,我知道你可以在每个字符串的末尾附加一个\0 ,而且有些函数会为你做这个,但我毫不怀疑这已经发生了。

使用缓冲区时,函数可能会失败,缓冲区保持不变。 你想要一个未知垃圾的缓冲区,或者什么都没有?

如果不进行初始化,可能会发生的一件事是您冒着泄漏敏感信息的风险。

未初始化的内存可能具有先前使用该内存的敏感内容。 可能是密码或加密密钥或私人电子邮件的一部分。 您的代码可能稍后在某处传输该缓冲区或结构,或将其写入磁盘,如果您只是部分填充它,则其余部分仍包含那些先前的内容。 当地址空间可以包含敏感信息时,某些安全系统需要将缓冲区归零 。

这篇文章经过大量编辑,以使其正确。 非常感谢Tyler McHenery指出我错过了什么。

 char buffer[1024] = {0}; 

将缓冲区中的第一个char设置为null,然后编译器将所有未初始化的chars扩展为0。 在这种情况下,两种技术之间的差异似乎归结为编译器是否为数组初始化生成更优化的代码,或者memset是否比生成的编译代码更快地优化。

以前我说过:

char buffer [1024] = {0};

将缓冲区中的第一个char设置为null。 该技术通常用于空终止字符串,因为处理空终止字符串的后续(非错误)函数会忽略超过第一个空值的所有数据。

这不是真的。 对于错误传达感到抱歉,再次感谢您的更正。

取决于你如何填写它:如果你计划在写入之前甚至可能阅读任何东西,那么为什么要这么麻烦? 它还取决于你将使用缓冲区:如果它将被视为一个字符串,那么你只需要将第一个字节设置为\0

 char buffer[1024]; buffer[0] = '\0'; 

但是,如果你将它用作字节流,那么整个数组的内容可能都是相关的,所以memset整个事物或将其设置为{ 0 }就像你的例子一样聪明。

我也使用memset(buffer,0,sizeof(buffer));

不使用它的风险是无法保证您使用的缓冲区是完全空的,可能存在可能导致不可预测行为的垃圾。

在malloc之后总是将memset设为0,这是一个非常好的做法。

是的,stdlib.h中定义的calloc()方法分配用零初始化的内存。

我不熟悉:

 char buffer[1024] = {0}; 

技术。 但假设它按照我的想法行事,这两种技术之间存在(潜在)差异。

第一个是在COMPILE时完成的,缓冲区将是可执行文件静态图像的一部分,因此在加载时为0。

后者将在运行时完成。

第一种可能会产生一些加载时间行为。 如果你有:

 char buffer[1024]; 

现代的加载器可能会“虚拟地”加载……也就是说,它不会占用文件中的任何实际空间,它只是指示加载程序在加载程序时创建一个块。 我对现代装载机的说法不太满意,如果这是真的与否。

但是如果你预先初始化它,那肯定需要从可执行文件中加载。

请注意,这些都不会对小型产品产生“真正的”性能影响。 他们可能没有任何“大”。 只是说这里有潜力,这两种技术实际上是在做一些截然不同的事情。