Bicycle, on balcony, north London

How to initialise structures to all-elements-zero-or-null

I understand that in the United States, the word `initialise' is spelled with a z. So for the benefit of Google users,

How to initialize structures to all-elements-zero-or-null

[ Home page | Random stuff ]

(This page is here because this is a question which seems to come up reasonably frequently, and it's useful to have the answer all written down in one place to refer to.)

When you declare a structure in `static storage' (meaning with the keyword static or at file scope), it is initialised with all numeric members zero and all pointers null. How should one achieve the same thing when initialising a structure in automatic or dynamic storage? Let's assume that this structure is defined by somebody else (say, POSIX), and in the normal fashion of such standards, all you know about the structure is that it contains at a minimum a certain set of elements; you do not know its exact contents.

Commmon answers look something like this:

int foo(void) {
    struct bar baz;
    memset(&baz, 0, sizeof baz);
}

int quux(void) {
    struct bar *fish;
    fish = calloc(sizeof *fish, 0);
}

Unfortunately, neither achieves the intended effect. There is no guarantee that the machine representation of zero is all-bits-zero, nor that the representation of a null pointer is all-bits-zero. (Note that the fact that you can initialise a null pointer by setting it to `0' doesn't signify here: the standard defines a `null pointer constant' as 0 or (void*)0, but makes no statement about its in-memory representation. It is not even guaranteed that different types of pointer have the same representation.)

The correct answer is to say

    struct bar baz = {0};

or, for dynamic storage, something like

    struct bar *baz, zz = {0};
    baz = malloc(sizeof *baz);
    *baz = zz;

-- note the ugly declaration of zz. You can do slightly better with a macro such as

/* alloc_struct TAG P
 * Enough memory to hold a struct TAG is allocated and assigned to P, and the
 * memory is initialised so that all numeric elements of *P are zero and all
 * pointer elements null. */
#define alloc_struct(tag, p)                    \
            do {                                \
                struct tag aszero ## tag = {0}; \
                p = malloc(sizeof *p);          \
                *p = aszero ## tag;             \
            } while (0)

Not very nice. If you're using GCC or another compiler which supports typeof, you can do

#define alloc_struct(p)                         \
            do {                                \
                typeof(*p) aszero_ ## p = {0};  \
                p = malloc(sizeof *p);          \
                *p = aszero_ ## p;              \
            } while (0)

-- but since the whole point of this exercise is to make the code portable, that's kind of pointless. As an alternative, you could declare a static `zeroed' instance of struct foo in the same header as the structure is defined, and then assign that to things later. None of these has the virtue of being elegant or pretty.

(It's also worth noting that if you use = {0} to initialise a structure which contains other aggregate types, current versions of GCC will issue a warning such as,

file.c: In function `func':
file.c:42: warning: missing braces around initializer
file.c:42: warning: (near initialization for `s.aggregate')

-- this is irritating, but there's not much that can be done about it.)

Justification

The typical objections to this approach are `You don't know what's in the structure, so you don't know whether the first element can be initialised to 0'; or, `What happens to the elements of the structure after the first?' Both are wrong. The basic game here is that:

  1. You don't have to initialise every element of a structure, but can initialise only the first one; you don't need nested {} even to initialise aggregate members of a structure.
  2. Anything in C can be initialised with = 0; this initialises numeric elements to zero and pointers null.

From the Standard (actually from the final draft, but I don't think these have changed since then),

[6.7.8.20] If the aggregate contains elements or members that are aggregates or unions, or if the first member of a union is an aggregate or union, these rules apply recursively to the subaggregates or contained unions. If the initializer of a subaggregate or contained union begins with a left brace, the initializers enclosed by that brace and its matching right brace initialize the elements or members of the subaggregate or the first member of the contained union. Otherwise, only enough initializers from the list are taken to account for the elements or members of the subaggregate or the first member of the contained union; any remaining initializers are left to initialize the next element or member of the aggregate of which the current subaggregate or contained union is a part.
[6.7.8.21] If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, or fewer characters in a string literal used to initialize an array of known size than there are elements in the array, the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration.

so struct foo bar = {0} always initialises the first element of the first subelement of bar as if it had been initialised with = 0 and initialises the rest `[in] the same [way] as objects that have static storage duration'.

It would be neater to be able to say struct foo bar = {}, and some compilers (e.g. GCC) permit this, but the ANSI grammar requires an initialiser list to be non-empty.

If you don't believe me, see, e.g., this article on comp.lang.c.

Who cares?

Arguably, only pedants like me. The portability argument is on the face of it quite a strong one, but actually I think it's a bit of a sham. After all, there's so much bad C code out there which just calls memset that you'd be mad to build a new processor or MMU which didn't use all-bits-zero as the representation of a zero numeric value or as an invalid pointer which can be used as a null pointer.


Copyright (c) 2002 Chris Lightfoot. All rights reserved.