When I initialize the array below all the output looks ok except for values[3]
. For some reason values[3]
initialized as values[0]+values[5]
It looks like you are subject to unspecified behavior here, since the order of evaluation of the initialization list expressions is unspecified, from the draft C99 standard section 6.7.8
:
The order in which any side effects occur among the initialization list expressions is unspecified.133)
and note 133 says:
In particular, the evaluation order need not be the same as the order of subobject initialization.
As far as I can tell, the normative text that backs up note 133
would be from section 6.5
:
Except as specified later [...] the order of evaluation of subexpressions and the order in which side effects take place are both unspecified.
and we can see that an intializer is a full-expression from 6.8
(emphasis mine):
A full expression is an expression that is not part of another expression or of a declarator. Each of the following is a full expression: an initializer; [...]
After looking back at one of my old C++ answers that covered sequence points within an initializer and which places the full-expression in a different place then I originally concluded, I realized the grammar in 6.7.8
contained initializer twice:
initializer:
assignment-expression
{ initializer-list }
{ initializer-list , }
initializer-list:
designationopt initializer
initializer-list , designationopt initializer
I originally did not notice this and thought the statement on full-expressions applied to the top element in the above grammar.
I now believe like C++ the full-expression applies to each initializer within the initializer-list which make my previous analysis incorrect.
Defect report 439 confirmed my suspicion that this was indeed the case, it contains the following example:
#include
#define ONE_INIT '0' + i++ % 3
#define INITIALIZERS [2] = ONE_INIT, [1] = ONE_INIT, [0] = ONE_INIT
int main()
{
int i = 0;
char x[4] = { INITIALIZERS }; // case 1
puts(x);
puts((char [4]){ INITIALIZERS }); // case 2
puts((char [4]){ INITIALIZERS } + i % 2); // case 3
}
and it says:
In every use of the INITIALIZERS macro, the variable i is incremented three times. In cases 1 and 2, there is no undefined behavior, because the increments are in expressions that are indeterminately sequenced with respect to one another, not unsequenced.
so each intializer within INITIALIZERS
is a full-expression.
Since this defect report is against C11 it is worth noting that C11 is more verbose then C99 in the normative text on this issue and it says:
The evaluations of the initialization list expressions are indeterminately sequenced with respect to one another and thus the order in which any side effects occur is unspecified.152)
There is undefined behavior in the case where the following expressions are evaluated before the respective elements in values
are assigned to:
values[0] + values[5]
or:
values[5]/10
This is undefined behavior since using an indeterminate value invokes undefined behavior.
In this specific case the simplest work-around would be to perform the calculations by hand:
int values[10] = {
[0]=197,[2]=-100,[5]=350,
[3]= 197 + 350,
[9]= 350/10
};
There are other alternatives such as doing the assignments to element 3
and 9
after the initialization.