归零记忆

ant*_*009 24 c

gcc 4.4.4 C89

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

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

char buffer[1024] = {0};
Run Code Online (Sandbox Code Playgroud)

这将使所有字节为零.

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

char buffer[1024];
.
.
memset(buffer, 0, sizeof(buffer));
Run Code Online (Sandbox Code Playgroud)

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

JSB*_*ոգչ 12

我非常喜欢

char buffer[1024] = { 0 };
Run Code Online (Sandbox Code Playgroud)

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

  • @Edwin Buck:即便如此,其他程序员很可能会用`memset`编写一个正确的版本,无论如何这可能都很好.此外,其他程序员一般不应该改变那些没有破坏的东西,如果*你*不在你的代码中使用它,那么你就会让其他人不熟悉这个习语的问题永久化. (2认同)
  • 这条规则很容易记住,一旦你知道它:“对象永远不会在 C 中部分初始化”。所以任何东西(数组、结构体等)要么完全初始化,要么根本没有初始化。 (2认同)

Tim*_*ost 12

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

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

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

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

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

  • "可能发生的最糟糕的事情是什么?" 一个非空终止的字符串或缓冲区溢出可以覆盖关键数据,粉碎你的堆栈指针,导致安全漏洞,让你被解雇,吃掉你的孩子,在非洲引发饥荒,煽动核战争,并召唤恐惧的Cthulhu.聪明的程序员用他拥有的每一件武器保护自己免受伤害.此外,我很确定没有商业C编译器实际上实现了规范的那部分,除了可能在调试版本中.未初始化的变量获得未初始化的内存. (10认同)
  • @JS Bangs-我可以向您保证,我的孩子和战斗部不会受到这种命运的影响。无关地,您在过去24小时内喝了多少咖啡或含咖啡因的饮料(以升或加仑计)? (2认同)

spo*_*son 10

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

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

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


jam*_*lin 8

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

  • 它提供了一个错误界限的机会.
  • 它提供了一个混淆论据的机会memset(例如memset(buf, sizeof buf, 0)代替memset(buf, 0, sizeof buf).

一般来说,= { 0 }也可以更好地初始化structs.它有效地初始化所有成员,就好像你已经编写= 0初始化每个成员一样.这意味着保证指针成员被初始化为空指针(可能不是全位 - 零,并且如果您使用过,则所有位为零memset).

另一方面,= { 0 }可以将填充位struct留作垃圾,因此如果您打算memcmp稍后用它来比较它们可能不合适.