Home > Back-end >  If I zero initialize a struct at the start of a loop using {0}, will it be zeroed out every iteratio
If I zero initialize a struct at the start of a loop using {0}, will it be zeroed out every iteratio

Time:01-27

Let's assume that I have a for loop, and a very large struct as a stack variable:

for (int x=0 ; x <10; x  )
{
    MY_STRUCT structVar = {0};
    …code using structVar…
}

Will every compiler actually zero out the struct at the start of every loop? Or do I need to use memset to zero it out?

This is a very large struct and I want to allocate it on the stack, and I need to make sure every member of it is zeroed out at the start of every iteration. So do I need to use memset?

I can manually inspect the executable that I compile, but I need to make sure if there is any standard for this, or it just depends on the compiler.

Note that this code does compile. I am using Visual Studio.

CodePudding user response:

Will every compiler actually zero out the struct at the start of every loop?

Any compiler that conforms to the C Standard will do this. From this Draft C11 Standard (bold emphasis mine):

6.8 Statements and blocks


3    A block allows a set of declarations and statements to be grouped into one syntactic unit. The initializers of objects that have automatic storage duration, and the variable length array declarators of ordinary identifiers with block scope, are evaluated and the values are stored in the objects (including storing an indeterminate value in objects without an initializer) each time the declaration is reached in the order of execution, as if it were a statement, and within each declaration in the order that declarators appear.

In the case of a for or while loop, a declaration/initializer inside the loop's scope block is reached repeatedly on each and every iteration of the loop.

CodePudding user response:

See https://port70.net/~nsz/c/c11/n1570.html#6.2.4, paragraph 6:

If an initialization is specified for the object, it is performed each time the declaration or compound literal is reached in the execution of the block; otherwise, the value becomes indeterminate each time the declaration is reached

CodePudding user response:

Will every compiler actually zero out the struct at the start of every loop?

Yes, or it will produce machine code with equivalent functionality ("observable behavior") as if you had performed a zero-out.

As long as you initialize one single member in the struct, then the rest of them will get set to zero/null ("as if they had static storage duration"). Similarly, any padding bytes added to the struct by the compiler will get set to zero. This is guaranteed by the C standard ISO:9899:2018 6.7.9 §10, §19 and §21.

Generally, the place where the zero-out actually occurs in the resulting executable depends on how the data is used. If you for example zero the struct at the beginning of the loop body, then write to various members and print it all in the end of the loop body, the compiler don't have many other choices but to zero-out everything at each lap of the loop. Example:

for (int x=0 ; x <10; x  )
{
    MY_STRUCT structVar = {0};
    ...
    structVar.foo = a;
    structVar.bar = b;
    printf("%d %d\n", structVar.foo, structVar.bar);
}

On the other hand, the compiler might in this case be smart enough to realize that the struct is just a pointless middle man and replace this all with the equivalent printf("%d %d\n", a, b);, meaning that the struct would be removed entirely from the machine code.

Overall, discussing optimizations like this can't be done without a specific use-case, compiler and target system.


Or do I need to use memset to zero it out?

No. MY_STRUCT structVar = {0}; is functionally 100% equivalent of memset(structVar, 0, sizeof structVar);.


This is a very large struct and I want to allocate it on the stack

That's a different matter than initialization. It is indeed unwise to allocate large objects on the stack. In that case consider replacing it with for example this:

MY_STRUCT* structVar = malloc(sizeof *structVar);
for (int x=0 ; x <10; x  )
{
  memset(structVar, 0, sizeof *structVar);  
  ...
}
free(structVar);

CodePudding user response:

For the variables (of automatic storage class) defined within the body of a loop, the variables are (notionally) recreated for each iteration of the loop and have indeterminate values for each iteration if not initialized.

Regarding use of the {0} initializer for an object with automatic storage duration, note the following:

  • As per 6.7.9/19 and 6.7.9/21, elements of the object that have no explicit initializer will be initialized implicitly the same as object of static storage duration. As per 6.7.9/10, those elements will be initialized to value zero or a null pointer as appropriate, and any padding will be initialized to zero bits.

Using memset(ptr, 0, size) sets size bytes from address ptr onwards to 0, but note the following:

  • For some unusual execution environments, a pointer object with all bytes zero might not represent a null pointer value (so might not compare equal to 0).

  • For some unusual execution environments, a floating point object with all bytes zero might not represent a valid floating point value or might not compare equal to 0.0.

In summary, using the {0} initializer is the most portable way to set all elements of the object to compare equal to 0, and to set all padding bits or bytes to 0. Using memset instead is generally OK except for some weird execution environments.

  •  Tags:  
  • Related