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

2022-01-18 00:00:00 initialization c visual-c++ struct c++

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

In Win32 API programming it's typical to use C structs with multiple fields. Usually only a couple of them have meaningful values and all others have to be zeroed out. This can be achieved in either of the two ways:

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

STRUCT theStruct = {};

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

The second variant looks cleaner - it's a one-liner, it doesn't have any parameters that could be mistyped and lead to an error being planted.

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

Does it have any drawbacks compared to the first variant? Which variant to use and why?

推荐答案

这两个结构的含义非常不同.第一个使用 memset 函数,该函数旨在将内存缓冲区设置为某个值.第二个初始化对象.让我用一点代码解释一下:

Those two constructs a very different in their meaning. The first one uses a memset function, which is intended to set a buffer of memory to certain value. The second to initialize an object. Let me explain it with a bit of code:

假设您的结构只有 POD 类型的成员(普通旧数据" - 请参阅 什么是 C++ 中的 POD 类型?)

Lets assume you have a structure that has members only of POD types ("Plain Old Data" - see What are POD types in C++?)

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 使用.由于您通常无法访问这些字节,因此对您来说没有区别.

In this case writing a POD_OnlyStruct t = {} or POD_OnlyStruct t; memset(&t, 0, sizeof t) doesn't make much difference, as the only difference we have here is the alignment bytes being set to zero-value in case of memset used. Since you don't have access to those bytes normally, there's no difference for you.

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

On the other hand, since you've tagged your question as C++, let's try another example, with member types different from 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 时,您将看到崩溃,因为该对象的所有内部memset 破坏了结构.

In this case using an expression like TestStruct t = {} is good, and using a memset on it will lead to crash. Here's what happens if you use memset - an object of type TestStruct is created, thus creating an object of type std::string, since it's a member of our structure. Next, memset sets the memory where the object b was located to certain value, say zero. Now, once our TestStruct object goes out of scope, it is going to be destroyed and when the turn comes to it's member std::string b you'll see a crash, as all of that object's internal structures were ruined by the memset.

所以,现实情况是,那些东西是非常不同的,尽管在??某些情况下您有时需要将整个结构 memset 归零,但始终重要的是确保您了解自己在做什么,并且不会像我们的第二个示例那样犯错误.

So, the reality is, those things are very different, and although you sometimes need to memset a whole structure to zeroes in certain cases, it's always important to make sure you understand what you're doing, and not make a mistake as in our second example.

我的投票 - 如果需要,only 对对象使用 memset,并使用 default 初始化 x = {} 在所有其他情况下.

My vote - use memset on objects only if it is required, and use the default initialization x = {} in all other cases.

相关文章