归零记忆

时间:2010-05-21 17:35:14

标签: c

gcc 4.4.4 C89

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

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

char buffer[1024] = {0};

这将使所有字节为零。

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

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

你有没有真正的理由要将记忆归零?不做的最坏情况是什么?

12 个答案:

答案 0 :(得分:12)

我非常喜欢

char buffer[1024] = { 0 };

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

答案 1 :(得分:12)

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

编辑(来自评论)根据您的工作,世界末日也是一种遥远的可能性。

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

c99包含有关默认初始化值的语言,但我不能使用任何类型的存储使gcc -std=c99同意这一点。

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

答案 2 :(得分:9)

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

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

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

答案 3 :(得分:8)

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

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

通常,= { 0 }更适合初始化struct。它有效地初始化了所有成员,就像您已经编写= 0来初始化每个成员一样。这意味着保证指针成员被初始化为空指针(which might not be all-bits-zero,并且如果您使用memset),则所有位为零。

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

答案 4 :(得分:3)

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

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

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

答案 5 :(得分:3)

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

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

答案 6 :(得分:3)

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

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

答案 7 :(得分:2)

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

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

但是,如果您将其用作字节流,那么整个数组的内容可能都是相关的,因此memset整个事物或将其设置为{ 0 }为在你的例子中是一个聪明的举动。

答案 8 :(得分:2)

此帖已经过大量编辑,以使其正确无误。非常感谢Tyler McHenery指出我错过了什么。

char buffer[1024] = {0};

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

之前我曾说过:

  

char buffer [1024] = {0};

     

将设置缓冲区中的第一个char   为空。这种技术很常见   用于空终止字符串,如   超过第一个null的所有数据都是   后来忽略了(非马车)   处理null终止的函数   字符串。

哪个不太正确。对于错误传达感到抱歉,再次感谢您的更正。

答案 9 :(得分:1)

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

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

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

答案 10 :(得分:1)

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

答案 11 :(得分:-1)

我不熟悉:

char buffer[1024] = {0};

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

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

后者将在RUN TIME完成。

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

char buffer[1024];

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

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

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