This is from creference:
Assignment operators' left operands must be unary (level-2 non-cast) expressions.
That reference page has 2 occurences of "level", i.e. the other is "precedence level". But precedence level 1 has "Function call" and precedence level 2 has "sizeof" which are both can't be assigned something to.
I can understand "non-cast" because cast may convert lvalue to rvalue.
Then what does that "level-2" mean?
This is from creference:
Assignment operators' left operands must be unary (level-2 non-cast) expressions.
That reference page has 2 occurences of "level", i.e. the other is "precedence level". But precedence level 1 has "Function call" and precedence level 2 has "sizeof" which are both can't be assigned something to.
I can understand "non-cast" because cast may convert lvalue to rvalue.
Then what does that "level-2" mean?
Share Improve this question asked Mar 30 at 11:07 An5DramaAn5Drama 7311 gold badge4 silver badges13 bronze badges1 Answer
Reset to default 6The reference to “level-2” is a reference to the row labeled “2” in the Precedence column in the preceding table.
This “level” is an invention of cppreference; it is not part of the C standard. (And so it is one reason cppreference is not a good reference.) The C standard does not specify precedences by levels or a table. Precedence is built into the formal grammar of the language.
In the formal C grammar, a cast-expression is one of:
- unary-expression
(
type-name)
cast-expression
A unary-expression is one of:
- postfix-expression
++
unary-expression--
unary-expression- unary-operator cast-expression, where unary-operator is one of
&
,*
,+
,-
,~
, or!
sizeof
unary-expressionsizeof
(
type-name)
alignof
(
type-name)
Comparing these to the cppreference table, we see its precedence level two items are all the operators of a unary-expression (omitting the operator-less postfix-expression) plus the cast operator. Therefore, the C standard’s unary-expression corresponds to all of cppreference’s level-2 operators except the cast. That is what cppreference is getting at with “level-2 non-cast”; taking its level-2 operators and removing the cast leaves the “unary” operators. (Cast is a unary operator,1 but it is not included in the unary-expression token in the formal C grammar. This is what prevents sizeof (int) * p
from being interpreted as sizeof ( (int *) p )
instead of (sizeof (int)) * p
, which is mentioned, but not explained, in cppreference’s note 2.)
cppreference’s statement that “Assignment operators' left operands must be unary (level-2 non-cast) expressions” refers to the fact that, in the C formal grammar, an assignment-expression is one of:
- conditional-expression
- unary-expression assignment-operator assignment-expression
Thus, the grammar requires that the left operand of an assignment operator be a unary-expression.
This is not the only requirement on the left operand of an assignment operator. C 2024 6.5.17.1 also states:
An assignment operator shall have a modifiable lvalue as its left operand.
This is not part of the formal grammar. It is stated separately as a constraint. So the left operand of an assignment operator must be a unary-expression and it must be a modifiable lvalue.
Footnote
1 Per Kernighan and Ritchie, The C Programming Language (second edition), 1988, page 45: “Finally, explicit type conversions can be forced (‘coerced’) in any expression, with a unary operator called a cast.”