tags:

views:

3251

answers:

5

In our code we used to have something like this:

   *(controller->bigstruct) = ( struct bigstruct ){ 0 };

This used to work great, and then we upgraded versions of GCC and suddenly started seeing stack overflows. Looking at the assembly, the old GCC code (2.x) was basically doing this:

memset(controller->bigstruct, 0, sizeof(struct bigstruct));

The new GCC (3.4.x) was doing this

   struct bigstruct temp = { 0 };
   controller->bigstruct = temp;

After reviewing the C99 spec, I could see why; C99 basically requires that anonymous structures exist on the stack. It's a good concept, but this structure was 4 Megabytes large, and only ever intended to exist on heap!

We've resorted to making our own 'initialize' function that explicitly sets the members, but that's ugly and a maintenance headache. I don't consider memset a proper solution, because I can't know that a bit-value of 0 is an appropriate zero value for the type ( nit-picking, I know, but there you are; I don't mind that the compiler does it, because it can know )

What is the "correct", or at least best, way to initialize a large structure like this?

To furthur clarify why I think memset isn't a solution: The rules of initialization of members not explicitly initialized are the same as static initialization, and are as follows: - If it has pointer type, it is initialized to a null pointer; - If it has arithmetic type, it is initialized to ( positive or unsigned ) zero; ...

'memset' will set the memory to bit-pattern zero, which isn't necessarily the same thing. Imagine a system that doesn't use IEEE floating point numbers. Unusual, but supported by C. The representation of 0.0 doesn't have to mean "all-bits zero", it could be anything convenient to the processor.

+4  A: 

If you don't want to use memset, you could always declare a static copy of your struct and use memcpy, which will give similar performance. This will add 4 megs to your program but is probably better than setting individual elements.

That said, if GCC was using memset, and it was good enough previously, I would suggest it is good enough now.

Shane MacLaughlin
As a `{0}` struct is essentially a sequence of zero bytes (unless you're on some really insane platform), this will NOT add 4M to the program. Global objects that are zero-initialized go to a different section and are not put in the program image - what's the point if the whole section is all-zero? Also, this static struct won't even use any memory at run-time, as the VM subsystem of the OS will map the whole memory range to a single system-wide zero-page with copy-on-write semantics.
slacker
+13  A: 

memset is the way to go. You do not have many alternatives.

Do something like:

#define InitStruct(var, type) type var; memset(&var, 0, sizeof(type))

So that you only have to:

InitStruct(st, BigStruct);

And then use st as usual...

I do not get how "0" is not a valid "0" type for a struct. The only way to "mass initialize" a struct is to set all of its memory to a value; otherwise you would have to make extra logic to tell it to use a specific bit pattern per member. The best "generic" bit pattern to use is 0.

Besides - this is the same logic that you used when doing

*(controller->bigstruct) = *( struct bigstruct ){ 0 };

Therefore I don't get your reluctance to use it :)

The first comment to this post made me do some research before I called him and idiot and I found this:

http://www.lysator.liu.se/c/c-faq/c-1.html

Very interesting; if I could vote-up a comment I would :)

That being said - your only option if you want to target archaic architectures with non-0 null values is still to do manual initialization to certain members.

Thanks Thomas Padron-McCarthy! I learned something new today :)

nlaq
The { 0 } in the source code does not necessarily translate to an all-zero bit pattern. For example, a 0 in the source code in a pointer context will be interpreted as a NULL pointer, but the NULL pointer need not be represented as all zero bits.
Thomas Padron-McCarthy
Or 0 may be a valid value for some index, and -1 represents an initial value of 'not found'. That's why C++ has a ctor.
Martin Beckett
You may want to wrap that macro in the usual "do { } while (0)" block...
dmckee
@dmckee: Wouldn't that kind of ruin the purpose of actually declaring a new variable, by wrapping the declaration in a new scope?
unwind
And also, in cases of single-line statements, it makes no sense to declare a var anyway - meaning the compile error you will get will just remind you that your doing something wrong :)
nlaq
A: 

hmm - first of all making an init function and setting each member explicitly IS THE RIGHT THING - it,s the way constructors in OO languages work.

and second - does anyone know a hardware that implements non IEEE floating point numbers ? - perhaps Commodore 64 or somethig ;-)

lbownik
dec alpha processor.dec vax processor.check for the term DFLOAT
EvilTeach
+4  A: 

Private initialization function is not ugly rather a good OO way to initialize objects (structs). I assume that your structure is not 4MB of pointers, so i would assume that the solution should be like this:

void init_big_struct(struct bigstruct *s)  
{  
    memset(s, 0, sizeof(struct bigstruct));  
    s->some_pointer = NULL; // Multiply this as needed  
}

From other hand our code is running on more then 20 embedded operating systems and large number of different hardwares, never meet any problem with just memset of the struct.

Ilya
To indent the code, just add more spaces. Your first, second, and last lines should begin with 4 spaces, and the other two should begin with 8 spaces.
Graeme Perrow
+3  A: 

As others have said, memset is the way to go. However, don't use memset on C++ objects, particularly those with virtual methods. The sizeof( foo ) will include the table of virtual function pointers, and doing a memset on that will cause serious grief.

If memset doesn't solve the problem by itself, simply do a memset and then initialize any members that should be non-zero (i.e. your non-IEEE floating point values).

Graeme Perrow
Good point: very true
nlaq