I just spent until about 1AM tracking down a bug in my code and what I found really surprised me. The actual code is very complex involving unions of structures containing unions of structures, etc. but I have distilled the issue down to the following simplified failing case.
What is happening is that the compiler [gcc 5.4.0] is changing the order of the execution of the designated initializers to match the order they appear in the structure. This does not cause any issues as long as you are initializing the structure with constants or variables that have no order dependency. Check out this following code. It shows that the compiler clearly reorders the designated initializers:
#include <stdio.h>
typedef const struct {
const size_t First_setToOne;
const size_t Second_setToThree;
const size_t Third_setToTwo;
const size_t Fourth_setToFour;
} MyConstStruct;
static void Broken(void)
{
size_t i = 0;
const MyConstStruct myConstStruct = {
.First_setToOne = ++i,
.Third_setToTwo = ++i,
.Second_setToThree = ++i,
.Fourth_setToFour = ++i,
};
printf("\nBroken:\n");
printf("First_setToOne should be 1, is %zd\n", myConstStruct.First_setToOne );
printf("Second_setToThree should be 3, is %zd\n", myConstStruct.Second_setToThree);
printf("Third_setToTwo should be 2, is %zd\n", myConstStruct.Third_setToTwo );
printf("Fourth_setToFour should be 4, is %zd\n", myConstStruct.Fourth_setToFour );
}
static void Fixed(void)
{
size_t i = 0;
const size_t First_setToOne = ++i;
const size_t Third_setToTwo = ++i;
const size_t Second_setToThree = ++i;
const size_t Fourth_setToFour = ++i;
const MyConstStruct myConstStruct = {
.First_setToOne = First_setToOne ,
.Third_setToTwo = Third_setToTwo ,
.Second_setToThree = Second_setToThree,
.Fourth_setToFour = Fourth_setToFour ,
};
printf("\nFixed:\n");
printf("First_setToOne should be 1, is %zd\n", myConstStruct.First_setToOne );
printf("Second_setToThree should be 3, is %zd\n", myConstStruct.Second_setToThree);
printf("Third_setToTwo should be 2, is %zd\n", myConstStruct.Third_setToTwo );
printf("Fourth_setToFour should be 4, is %zd\n", myConstStruct.Fourth_setToFour );
}
int main (int argc, char *argv[])
{
(void)argc;
(void)argv;
Broken();
Fixed();
return(0);
}
The output is as follows:
Broken:
First_setToOne should be 1, is 1
Second_setToThree should be 3, is 2
Third_setToTwo should be 2, is 3
Fourth_setToFour should be 4, is 4
Fixed:
First_setToOne should be 1, is 1
Second_setToThree should be 3, is 3
Third_setToTwo should be 2, is 2
Fourth_setToFour should be 4, is 4
I suspected the optimizer but I tried the same code using every possible optimization level and the reordering still occurs. So this issue is in the base compiler.
I have a solution so this is more of a warning for others and a general question.
Has anyone else ever see or noticed this issue?
Is this the expected/specified behavior?
C99 standard allows side effects to be applied in any order:
6.7.8.23: The order in which any side effects occur among the initialization list expressions is unspecified.
A footnote provides further clarification:
In particular, the evaluation order need not be the same as the order of subobject initialization.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With