Memset()或值初始化为零的结构?

在 Win32API 编程中,通常使用具有多个字段的 Cstruct。通常只有少数几个有意义的值,其他所有值都必须被归零。这可以通过以下两种方式中的任何一种实现:

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

或者

STRUCT theStruct = {};

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

与第一个版本相比,它有什么缺点吗? 使用哪个版本? 为什么?

118661 次浏览

I would use value initialization because it looks clean and less error prone as you mentioned. I don't see any drawback in doing it.

You might rely on memset to zero out the struct after it has been used though.

not that it's common, but I guess the second way also has the benefit of initializing floats to zero. While doing a memset would certainly not

The value initialization because it can be done at compile time.
Also it correctly 0 initializes all POD types.

The memset() is done at runtime.
Also using memset() is suspect if the struct is not POD.
Does not correctly initialize (to zero) non int types.

Depending on the structure members, the two variants are not necessarily equivalent. memset will set the structure to all-bits-zero whereas value initialization will initialize all members to the value zero. The C standard guarantees these to be the same only for integral types, not for floating-point values or pointers.

Also, some APIs require that the structure really be set to all-bits-zero. For instance, the Berkeley socket API uses structures polymorphically, and there it is important to really set the whole structure to zero, not just the values that are apparent. The API documentation should say whether the structure really needs to be all-bits-zero, but it might be deficient.

But if neither of these, or a similar case, applies, then it's up to you. I would, when defining the structure, prefer value initialization, as that communicates the intent more clearly. Of course, if you need to zeroize an existing structure, memset is the only choice (well, apart from initializing each member to zero by hand, but that wouldn't normally be done, especially for large structures).

If there are lots of pointer members and you are likely to add more in the future, it can help to use memset. Combined with appropriate assert(struct->member) calls you can avoid random crashes from trying to deference a bad pointer that you forgot to initialize. But if you're not as forgetful as me, then member-initialization is probably the best!

However, if your struct is being used as part of a public API, you should get client code to use memset as a requirement. This helps with future proofing, because you can add new members and the client code will automatically NULL them out in the memset call, rather than leaving them in a (possibly dangerous) uninitialized state. This is what you do when working with socket structures for example.

In some compilers STRUCT theStruct = {}; would translate to memset( &theStruct, 0, sizeof( STRUCT ) ); in the executable. Some C functions are already linked in to do runtime setup so the compiler have these library functions like memset/memcpy available to use.

If your struct contains things like :

int a;
char b;
int c;

Then bytes of padding will be inserted between "b" and "c". memset() will zero those, the other way will not, so there will be 3 bytes of garbage (if your ints are 32 bits). If you intend to use your struct to read/write from a file, this might be important.

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:

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

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.

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

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.

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.

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