memset()或值初始化将结构清零?

时间:2010-01-04 10:40:41

标签: c++ c visual-c++ struct initialization

在Win32 API编程中,通常使用带有多个字段的C struct。通常只有少数几个具有有意义的值,而其他所有值都必须归零。这可以通过以下两种方式之一实现:

STRUCT theStruct;
memset( &theStruct, 0, sizeof( STRUCT ) );

STRUCT theStruct = {};

第二个变体看起来更干净 - 它是一个单行,它没有任何可能输入错误的参数并导致错误被种植。

与第一个变体相比,它有任何缺点吗?使用哪种变体以及为什么?

8 个答案:

答案 0 :(得分:82)

这两个构造的非常的含义不同。第一个使用memset函数,该函数用于将内存缓冲区设置为特定值。第二个初始化一个对象。让我用一些代码解释一下:

让我们假设您的结构只包含的POD类型

struct POD_OnlyStruct
{
    int a;
    char b;
};

POD_OnlyStruct t = {};  // OK

POD_OnlyStruct t;
memset(&t, 0, sizeof t);  // OK as well

在这种情况下,写POD_OnlyStruct t = {}POD_OnlyStruct t; memset(&t, 0, sizeof t)没有太大区别,因为我们这里唯一的区别是 alignment 字节被设置为零值如果使用memset。由于您无法正常访问这些字节,因此对您没有任何区别。

另一方面,由于您已将问题标记为C ++,让我们尝试另一个示例,其成员类型与POD不同

struct TestStruct
{
    int a;
    std::string b;
};

TestStruct t = {};  // OK

{
    TestStruct t1;
    memset(&t1, 0, sizeof t1);  // ruins member 'b' of our struct
}  // Application crashes here

在这种情况下,使用类似TestStruct t = {}的表达式是好的,并且在其上使用memset将导致崩溃。如果使用memset,会发生以下情况 - 创建类型为TestStruct的对象,从而创建类型为std::string的对象,因为它是我们结构的成员。接下来,memset将对象b所在的内存设置为特定值,即零。现在,一旦我们的TestStruct对象超出范围,它将被销毁,当转向它的成员std::string b时,你会看到一个崩溃,因为所有该对象的内部结构都被{{破坏了1}}。

所以,现实是,那些事情是非常不同的,虽然你有时需要memset整个结构在某些情况下为零,但确保你理解这一点总是很重要你正在做什么,而不是像我们的第二个例子那样犯错。

我的投票 - 如果需要,只在对象上使用memset,并在所有其他情况下使用默认初始化memset。< / p>

答案 1 :(得分:29)

根据结构成员的不同,这两种变体不一定相同。 memset将结构设置为all-bits-zero,而value initialization将所有成员初始化为零值。 C标准保证这些仅对于整数类型是相同的,而不是浮点值或指针。

此外,某些API要求结构确实设置为全位为零。例如,Berkeley套接字API以多态方式使用结构,并且真正将整个结构设置为零非常重要,而不仅仅是显而易见的值。 API文档应该说明结构是否真的需要全位为零,但它可能是不足的。

但是,如果这些或类似情况都不适用,那么这取决于你。在定义结构时,我会更喜欢值初始化,因为它更清楚地传达了意图。当然,如果您需要将现有结构归零,memset是唯一的选择(除了手动将每个成员初始化为零,但通常不会这样做,特别是对于大型结构)。 / p>

答案 2 :(得分:10)

如果您的结构包含以下内容:

int a;
char b;
int c;

然后将在“b”和“c”之间插入填充字节。 memset()将为零,其他方式不会,因此将有3个字节的垃圾(如果你的整数是32位)。如果您打算使用结构从文件读取/写入,这可能很重要。

答案 3 :(得分:7)

我会使用值初始化,因为它看起来很干净,并且不像你提到的那样容易出错。我认为没有任何缺点。

在使用结构后,您可能依赖memset将结构清零。

答案 4 :(得分:5)

不是很常见,但我猜第二种方式也有将初始化浮点数归零的好处。虽然做一个memset肯定不会

答案 5 :(得分:3)

在某些编译器中,STRUCT theStruct = {};会在可执行文件中转换为memset( &theStruct, 0, sizeof( STRUCT ) );。一些C函数已经链接到运行时设置中,因此编译器可以使用这些库函数,如memset / memcpy。

答案 6 :(得分:2)

值初始化,因为它可以在编译时完成 它也正确地初始化所有POD类型。

memset()在运行时完成 如果结构不是POD,也可以使用memset() 未正确初始化(归零)非int类型。

答案 7 :(得分:-1)

如果有很多指针成员,并且您将来可能会添加更多指针成员,那么使用memset会有所帮助。结合适当的assert(struct->member)调用,您可以避免随机崩溃尝试依赖您忘记初始化的错误指针。但如果你不像我一样健忘,那么成员初始化可能是最好的!

但是,如果您的结构被用作公共API的一部分,您应该获取客户端代码以使用memset作为要求。这有助于将来进行校对,因为您可以添加新成员,并且客户端代码将在memset调用中自动将它们清空,而不是将它们置于(可能是危险的)未初始化状态。这就是您在使用套接字结构时所做的事情。