Reputation: 9115
Is it required that the ternary operator be evaluated statically whenever possible, or can the compiler defer it to later, and therefore reject a program which would depend on it being evaluated statically?
More precisely, here's an ambiguous fragment of a program:
char b[sizeof(a) > 10 ? 10 : sizeof(a)] = {0};
Depending on whether a
is a variable-length array or not, the fragment is invalid or not. If a
is a VLA, then sizeof(a)
is not determined statically, and therefore the initialization cannot take place:
int main(int argc, char **argv) {
int a[argc];
char b[sizeof(a) > 10 ? 10 : sizeof(a)] = {0}; // invalid
return 0;
}
error: variable-sized object may not be initialized
But if a
is not a VLA, then everything can be evaluated statically:
int main() {
int a[42];
char b[sizeof(a) > 10 ? 10 : sizeof(a)] = {0}; // valid
return 0;
}
My question is: are standard-conforming compilers required to evaluate statically whenever possible, and therefore must accept the second program, or are they allowed to "defer it to dynamic evaluation" and may reject it?
Upvotes: 4
Views: 105
Reputation: 224310
“Evaluated statically” is not defined by the C standard.
If the array size is an integer constant expression, the array has a complete type with known constant size. Otherwise, it is a variable length array.
C 2018 6.6 6 defines integer constant expression:
An integer constant expression shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants,
sizeof
expressions whose results are integer constants,_Alignof
expressions, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to thesizeof
or_Alignof
operator.
So, in char b[sizeof(a) > 10 ? 10 : sizeof(a)]
, the question is whether sizeof(a)
has a result that is an integer constant. C 6.5.3.4 2 tells us:
The
sizeof
operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. The size is determined from the type of the operand. The result is an integer. If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.
Thus, if a
is not a variable length array, the result is an integer constant. If a
is a variable length array, the result is an integer but not, implicitly, an integer constant.
Thus, if a
is a variable length array, then char b[sizeof(a) > 10 ? 10 : sizeof(a)]
declares a variable length array. The use of the ternary operator is not relevant, because the determination is based on the fact that the expression contains operands that are not in the list given in 6.6 6, and not on whether a ternary operator is present.
If a
is not a variable length array, then char b[sizeof(a) > 10 ? 10 : sizeof(a)]
declares an array of known constant size. Again, the ternary operator is not relevant; the determination in 6.6 6 does not mention it. So the array size is a constant integer expression, and a conforming C implementation should accept b
as an array of known constant size and permit its initialization.
There is one exception to the above. C 2018 6.6 10 says:
An implementation may accept other forms of constant expressions.
Thus, an implementation could in theory define sizeof(a) > 10 ? 10 : sizeof(a)
to be a constant expression. Reliance on this would of course not be portable.
Upvotes: 4
Reputation: 225477
In the second case:
int a[42];
char b[sizeof(a) > 10 ? 10 : sizeof(a)] = {0};
The expression sizeof(a) > 10 ? 10 : sizeof(a)
is considered a constant expression as defined in section 6.6 of the C standard, and is also an integer constant expression.
From section 6.6p3 states the constraints on a constant expression:
Constant expressions shall not contain assignment, increment, decrement, function-call, or comma operators, except when they are contained within a subexpression that is not evaluated.
The above expression does not contain any of the disallowed operators.
Section 6.6p6 further details integer constant expressions:
An integer constant expression 117) shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants,
sizeof
expressions whose results are integer constants,_Alignof
expressions, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to thesizeof
or_Alignof
operator .
And footnote 117:
117) An integer constant expression is required in a number of contexts such as the size of a bit-field member of a structure, the value of an enumeration constant, and the size of a non-variable length array. Further constraints that apply to the integer constant expressions used in conditional-inclusion preprocessing directives are discussed in 6.10.1
Because a
is not a variable length array, sizeof(a)
evaluates to an integer constant. And because sizeof(a) > 10 ? 10 : sizeof(a)
contains only 10
which is an integer constant, sizeof(a)
which evaluates to an integer constant, and the operators ?:
and >
which are not disallowed operators, the entire expression is considered an integer constant expression and can be used as the size of a non-variable length array, meaning you can initialize it.
Upvotes: 5