views:

854

answers:

4

Whenever I look at real code or example socket code in books, man pages and websites, I almost always see something like:

struct sockaddr_in foo;
memset(&foo, 0, sizeof foo); 
/* or bzero(), which POSIX marks as LEGACY, and is not in standard C */
foo.sin_port = htons(42);

instead of:

struct sockaddr_in foo = { 0 }; 
/* if at least one member is initialized, all others are set to
   zero (as though they had static storage duration) as per 
   ISO/IEC 9899:1999 6.7.8 Initialization */ 
foo.sin_port = htons(42);

or:

struct sockaddr_in foo = { .sin_port = htons(42) }; /* New in C99 */

or:

static struct sockaddr_in foo; 
/* static storage duration will also behave as if 
   all members are explicitly assigned 0 */
foo.sin_port = htons(42);

The same can also be found for setting struct addrinfo hints to zero before passing it to getaddrinfo, for example.

Why is this? As far as I understand, the examples that do not use memset are likely to be the equivalent to the one that does, if not better. I realize that there are differences:

  • memset will set all bits to zero, which is not necessarily the correct bit representation for setting each member to 0.
  • memset will also set padding bits to zero.

Are either of these differences relevant or required behavior when setting these structs to zero and therefore using an initializer instead is wrong? If so, why, and which standard or other source verifies this?

If both are correct, why does memset/bzero tend to appear instead of an initializer? Is it just a matter of style? If so, that's fine, I don't think we need a subjective answer on which is better style.

The usual practice is to use an initializer in preference to memset precisely because all bits zero is not usually desired and instead we want the correct representation of zero for the type(s). Is the opposite true for these socket related structs?

In my research I found that POSIX only seems to require sockaddr_in6 (and not sockaddr_in) to be zeroed at http://www.opengroup.org/onlinepubs/000095399/basedefs/netinet/in.h.html but makes no mention of how it should be zeroed (memset or initializer?). I realise BSD sockets predate POSIX and it is not the only standard, so are their compatibility considerations for legacy systems or modern non-POSIX systems?

Personally, I prefer from a style (and perhaps good practice) point of view to use an initializer and avoid memset entirely, but I am reluctant because:

  • Other source code and semi-canonical texts like UNIX Network Programming use bzero (eg. page 101 on 2nd ed. and page 124 in 3rd ed. (I own both)).
  • I am well aware that they are not identical, for reasons stated above.
A: 

Either one is correct as many have pointed out. Additionally you can allocate these structures with calloc which already returns a zeroed memory block.

lothar
This doesn't answer any part of my question, but yes I'm aware that calloc would do the same. I doubt it's used much, anyway.
Chris Young
@Chris Rephrased the answer
lothar
+2  A: 

One problem with the partial initializers approach (that is '{ 0 }') is that GCC will warn you that the initializer is incomplete (if the warning level is high enough; I usually use '-Wall' and often '-Wextra'). With the designated initializer approach, that warning should not be given, but C99 is still not widely used - though these parts are fairly widely available, except, perhaps, in the world of Microsoft.

I tend to favour an approach:

static const struct sockaddr_in zero_sockaddr_in;

Followed by:

struct sockaddr_in foo = zero_sockaddr_in;

The omission of the initializer in the static constant means everything is zero - but the compiler won't witter (shouldn't witter). The assignment uses the compiler's innate memory copy which won't be slower than a function call unless the compiler is seriously deficient.

Jonathan Leffler
Thanks, with which flags can I reproduce this gcc warning? And why does it warn, given that it's common practice to initialize aggregates completely to zero with a single { 0 }?
Chris Young
Ah, I see -Werror does the job. You do indeed provide a good case against { 0 }. Is this likely the reason why Stevens et al didn't use it?
Chris Young
I think it unlikely that Stevens et al were worried about this level of warning. I think it is a more recent addition to GCC anyway; I have code that I've not changed in some years that now gets warnings that I'd've fixed had it been a warning 'back then'.
Jonathan Leffler
+1  A: 

"struct sockaddr_in foo = { 0 };" is only valid for the first time, whereas "memset(&foo, 0, sizeof foo);" will clear it each time the function is run.

Robert
If foo is a local variable, it is valid each time. If foo is global or static, then you are correct.
Jonathan Leffler
An object with automatic storage duration plus an initializer will be initialized every time it's declared. This is correct for a static one, but these structs are typically not declared static.
Chris Young
This is only a problem if the struct is declared static, which it shouldn't be without good reason.
Dave
+1  A: 

There shouldn't be a problem with either approach -- the values of the padding bytes shouldn't matter. I suspect that the use of memset() stems from earlier use of the Berkeley-ism bzero(), which may have predated the introduction of struct initializers or been more efficient.

Dave
I suspect you're right - this use of bzero() is so old that many programmers just treat it as idiomatic.
Alnitak