数据结构的有效初始化通常是将所有成员设置为零。即使使用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在跟进中提出this question,我认为这是这个问题的一个有趣推论。
答案 0 :(得分:23)
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通过引入复合文字解决了这个问题。类似的功能也出现在C ++中。)因此,您可能会看到许多程序员使用memset
以便在中间归零C89 / 90或C ++的代码。然而,我要说正确的做法仍然是memset
而不是
some_struct s;
...
{
const some_struct ZERO = { 0 };
s = ZERO;
}
...
即。通过在代码中间引入一个“虚构”块,即使它在第一眼看起来可能看起来不太漂亮。当然,在C ++中没有必要引入一个块。
至于实际差异......你可能会听到有些人说memset
在实践中会产生相同的结果,因为实际上物理的全零位模式是用于表示零值的所有类型。但是,这通常不是真的。一个能够证明典型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
和普通= { 0 }
初始值设定项之间的实际差异。
答案 1 :(得分:15)
some_struct s = { 0 };
保证可以正常工作; memset
依赖于实施细节,最好避免使用。
答案 2 :(得分:6)
如果struct包含指针,则memset
生成的所有位0的值可能与在C(或C ++)代码中为其分配0
的值不同,即{ {1}}指针。
(NULL
和floats
也可能是这种情况,但我从未遇到过。但是,我不认为这些标准会保证它们在{{1}时变为零或者。)
编辑:从更实际的角度来看,我仍然会说尽可能避免使用doubles
,因为它是一个额外的函数调用,写入时间更长,并且(在我看来)意图不如memset
明确。
答案 3 :(得分:5)
根据编译器优化,可能存在一些阈值,其中memset更快,但通常远高于基于堆栈的变量的正常大小。在带有虚拟表的C ++对象上使用memset当然很糟糕。
答案 4 :(得分:4)
我找到了一个很好的解决方案:
template<typename T> void my_zero(T& e) {
static T dummy_zero_object;
e = dummy_zero_object;
}
my_zero(s);
这不仅对基本类型和用户定义类型是正确的,而且它也初始化了为其定义默认构造函数的类型,但没有初始化所有成员变量 - 尤其是包含非平凡的类{ {1}}成员。
答案 5 :(得分:3)
唯一的实用区别在于={0};
语法更清楚地说“将其初始化为空”(至少对我来说似乎更清楚)。
纯粹从理论上讲,有一些情况memset
可能会失败,但据我所知,它们实际上就是这样:理论上的。 OTOH,鉴于它在理论上和都不如实际观点,我很难弄清楚为什么有人想要memset
来完成这项任务。
答案 6 :(得分:3)
我从来没有理解将所有东西设置为零的神秘善意,即使它被定义似乎也不太可取。由于这被标记为C ++,因此初始化的正确解决方案是为结构或类提供构造。
答案 7 :(得分:3)
希望据了解,这仅适用于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
给定一个指向其构造函数的空指针。然而,第二个做你想要的:它创建一个空字符串。
答案 8 :(得分:2)
我认为初始化更能说明你实际在做什么。您正在初始化结构。当新标准出来时,初始化的方式会得到更多的使用(用{}初始化容器是值得期待的)。 memset方式稍微容易出错,并且不能清楚地表明你正在做什么。单独编程时可能不会占很多,但在团队工作时意味着很多。
对于使用c ++,memset,malloc&amp;合。是非常深奥的生物。我自己遇到过一些。
答案 9 :(得分:2)
清除结构的最佳方法是单独设置每个字段:
struct MyStruct
{
std::string name;
int age;
double checking_account_balance;
void clear(void)
{
name.erase();
age = 0;
checking_account_balance = 0.0;
}
};
在上面的示例中,定义了clear
方法将所有成员设置为已知状态或值。由于memset
和std::fill
类型,std::string
和double
方法可能无效。更强大的程序可以单独清除每个字段。
我更喜欢拥有一个更强大的计划,而不是花更少的时间打字。
答案 10 :(得分:0)
bzero
功能是另一种选择。
#include <strings.h>
void bzero(void *s, size_t n);
答案 11 :(得分:0)
在C中,我更喜欢使用{0,}来表示等效的memset()。 然而gcc警告这种用法:(详情在这里: http://www.pixelbeat.org/programming/gcc/auto_init.html
在C ++中,它们通常是等效的,但与C ++一样 有一些角落要考虑(在其他答案中注明)。