将对象初始化为全零

通常,数据结构的有效初始化是将所有成员设置为零。 即使使用C ++进行编程,也可能需要与外部API进行交互。

以下是否有任何实际区别:

some_struct s; memset(&s, 0, sizeof(s)); 

简单地说

 some_struct s = { 0 }; 

人们发现自己都使用这两种方法,选择哪种方法更适合给定的应用程序? (希望据了解,这只适用于POD结构;如果该结构中存在C ++ std :: string,则会遇到各种各样的破坏。)

对于我自己来说,作为一个不使用memset的C ++程序员,我从不确定函数签名,所以我发现第二个例子更容易使用,除了更少打字,更紧凑,甚至更多很明显,因为它在声明中说“这个对象被初始化为零”而不是等待下一行代码并且看到,“哦,这个对象是零初始化的”。

在C ++中创建类和结构时,我倾向于使用初始化列表; 我很好奇人们对上面两个“C风格”初始化的想法,而不是与C ++中可用的内容进行比较,因为我怀疑我们很多人都与C库接口,即使我们自己主要用C ++编写代码。

编辑: Neil Butterworth在后续提出这个问题 ,我认为这是这个问题的一个有趣的推论。

memset实际上从来都不是正确的方法。 是的,存在实际差异(见下文)。

在C ++中,不是所有东西都可以用文字0初始化(枚举类型的对象不能),这就是为什么在C ++中常见的习语是

 some_struct s = {}; 

而在C语言中,成语是

 some_struct s = { 0 }; 

注意,在C中, = { 0 }可以称为通用零初始化器 。 它可以与几乎任何类型的对象一起使用,因为{}封闭的初始化器也允许使用标量对象

 int x = { 0 }; /* legal in C (and in C++) */ 

这使得= { 0 }在通用类型无关的C代码(例如,与类型无关的宏)中很有用。

C89 / 90和C ++中= { 0 }初始值设定项的缺点是它只能用作声明的一部分。 (C99通过引入复合文字解决了这个问题。类似的function也出现在C ++中。)因此,您可能会看到许多程序员使用memset以便在C89 / 90或C ++中间将某些内容归零。 然而,我会说正确的方法仍然没有memset ,而是有类似的东西

 some_struct s; ... { const some_struct ZERO = { 0 }; s = ZERO; } ... 

即在代码中间引入一个“虚构”块,即使第一眼看上去看起来不太漂亮。 当然,在C ++中没有必要引入块。

至于实际差异……你可能会听到有人说memset在实践中会产生相同的结果,因为实际上物理的全零位模式是用来表示所有类型的零值。 但是,这通常不正确。 一个能够certificate典型C ++实现差异的直接示例是指向数据成员的指针类型

 struct S; ... int S::*p = { 0 }; assert(p == NULL); // this assertion is guaranteed to hold memset(&p, 0, sizeof p); assert(p == NULL); // this assertion will normally fail 

发生这种情况是因为典型的实现通常使用全一位模式( 0xFFFF... )来表示此类型的空指针。 上面的例子演示了归零memset和normal = { 0 }初始化器之间的实际实际差异。

some_struct s = { 0 }; 保证工作; memset依赖于实现细节,最好避免使用。

如果struct包含指针,则memset生成的所有位0的值可能与在C(或C ++)代码中为其分配0 (即NULL指针)不同。

floatsdoubles也可能是这种情况,但我从未遇到过。但是,我认为这些标准也不能保证它们在memset变为零。)

编辑:从一个更实用的角度来看,我仍然会说尽可能避免使用memset ,因为它是一个额外的函数调用,写入时间更长,并且(在我看来)意图不如= { 0 }更清晰。

根据编译器优化,可能存在一些阈值,其中memset更快,但通常远高于基于堆栈的变量的正常大小。 在带有虚拟表的C ++对象上使用memset当然很糟糕。

我找到了一个很好的解决方案:

 template void my_zero(T& e) { static T dummy_zero_object; e = dummy_zero_object; } my_zero(s); 

这不仅对基本类型和用户定义类型有正确的作用,而且它还初始化了为其定义默认构造函数但未初始化所有成员变量的类型 – 尤其是包含非平凡union成员的类。

唯一的实际区别是={0}; 语法更清楚地说“初始化为空”(至少对我来说似乎更清楚)。

从理论上讲,有一些情况下memset可能会失败,但据我所知,它们实际上只是:理论上的。 OTOH,鉴于它从理论实践的角度来看都很差,我很难弄清楚为什么有人会想让memset用于这个任务。

我从来没有理解将所有东西设置为零的神秘善意,即使它被定义似乎也不太可取。 由于这被标记为C ++,因此初始化的正确解决方案是为结构或类提供构造。

希望据了解,这仅适用于POD结构; 如果该结构中存在C ++ std :: string,则会出现编译器错误。

不,你不会 。 如果你使用memset ,那么最好你会崩溃,最糟糕的是你得到一些胡言乱语。 = { }方式可以非常好地用于非POD结构,只要它们是聚合。 = { }方式是获取C ++的最佳方式。 请注意,C ++中没有理由将0到其中,也不推荐使用,因为它大大减少了可以使用它的情况

 struct A { std::string a; int b; }; int main() { A a = { 0 }; A a = { }; } 

第一个不会做你想要的:它将尝试从C字符串创建一个std::string string给定一个指向其构造函数的空指针。 然而,第二个做你想要的:它创建一个空字符串。

我认为初始化更清楚你实际在做什么。 您正在初始化结构。 当新标准出来时,初始化的方式将得到更多的使用(用{}初始化容器是值得期待的)。 memset方式稍微容易出错,并且不能清楚地传达您正在做的事情。 单独编程时这可能不会占很多,但在团队工作时意味着很多。

对于一些使用c ++,memset,malloc&co的人。 是非常深奥的生物。 我自己遇到过一些。

清除结构的最佳方法是单独设置每个字段:

 struct MyStruct { std::string name; int age; double checking_account_balance; void clear(void) { name.erase(); age = 0; checking_account_balance = 0.0; } }; 

在上面的示例中,定义了一个clear方法,将所有成员设置为已知状态或值。 由于std::stringdouble类型, memsetstd::fill方法可能无法工作。 更强大的程序可以单独清除每个字段。

我更喜欢拥有一个更强大的程序而不是花更少的时间打字。

bzerofunction是另一种选择。

 #include  void bzero(void *s, size_t n); 

在CI中,更喜欢使用{0,}来表示等效的memset()。 然而,gcc警告这种用法:(详细信息: http : //www.pixelbeat.org/programming/gcc/auto_init.html

在C ++中,它们通常是等价的,但与C ++一样,还有一些需要考虑的极端情况(在其他答案中注明)。