memset
is practically never the right way to do it. And yes, there is a practical difference (see below).
In C++ not everything can be initialized with literal 0
(objects of enum types can’t be), which is why in C++ the common idiom is
some_struct s = {};
while in C the idiom is
some_struct s = { 0 };
Note, that in C the = { 0 }
is what can be called the universal zero initializer. It can be used with objects of virtually any type, since the {}
-enclosed initializers are allowed with scalar objects as well
int x = { 0 }; /* legal in C (and in C++) */
which makes the = { 0 }
useful in generic type-independent C code (type-independent macros for example).
The drawback of = { 0 }
initializer in C89/90 and C++ is that it can only be used as a part of declaration. (C99 fixed this problem by introducing compound literals. Similar functionality is coming to C++ as well.) For this reason you might see many programmers use memset
in order to zero something out in the middle of C89/90 or C++ the code. Yet, I’d say that the proper way to do is still without memset
but rather with something like
some_struct s;
...
{
const some_struct ZERO = { 0 };
s = ZERO;
}
...
i.e. by introducing a “fictive” block in the middle of the code, even though it might not look too pretty at the first sight. Of course, in C++ there’s no need to introduce a block.
As for the practical difference… You might hear some people say that memset
will produce the same results in practice, since in practice the physical all-zero bit pattern is what is used to represent zero values for all types. However, this is generally not true. An immediate example that would demonstrate the difference in a typical C++ implementation is a pointer-to-data-member type
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
This happens because a typical implementation usually uses the all-one bit pattern (0xFFFF...
) to represent the null pointer of this type. The above example demonstrates a real-life practical difference between a zeroing memset
and a normal = { 0 }
initializer.