The expression b = 5 << sizeof(a );
is an undefined behavior. But is c = a && b << --a;
or c = a || b << a ;
also an undefined behavior? I think it is undefined behavior, but I am not sure.
CodePudding user response:
He said "we should not have any side effects within the operands of an sizeof operator" [From a comment, apparently the reason
b = 5 << sizeof(a );
was said to have undefined behavior.]
If the teacher said this because such side effects might give rise to undefined behavior, they are wrong. The operand of sizeof
is not evaluated unless it is a variable length array type (C 2018 6.5.3.4 2), so no side effects occur for sizeof(a )
. Further, even if the expression were evaluated, there is no rule that mere presence of side effects in a sizeof
operand causes undefined behavior.
None of the displayed statements have undefined behavior per se. (Some definitions and values for a
, b
, and c
might give rise to undefined behavior.)
In c = a && b << --a;
, the left operand of &&
, a
, is specified to be evaluated before the right operand, b << --a
, and there is a sequence point between them (C 2018 6.5.13 4), so the fact that --a
modifies a
and a
is also used in the left operand does not trigger the rule (C 2018 6.5 2) that behavior is undefined if a side effect on a scalar object is unsequenced relative to a use of its value.
Similarly, in c = a || b << a ;
, there is a sequence point between evaluation of the left operand a
and the right operand b << a
(C 2018 6.5.14 4).
CodePudding user response:
This statement
b = 5 << sizeof(a );
is a valid expression statement. There is no undefined behavior. Pay attention to that the expression used in the operator sizeof
is not evaluated.
The undefined behavior can occur in this statement in one case (the C Standard. 6.5.7 Bitwise shift operators)
If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
But the type size_t
of an expression with the sizeof
operator is an unsigned integer type a value of which can not be negative.
In the other two statements
c = a && b << --a;
and
c = a || b << a ;
that are equivalent to
c = ( a ) && ( b << --a );
and
c = ( a ) || ( b << a );
neither one has undefined behavior because there is a sequence point after the evaluation of the first operand of the logical operators AND and OR.
From the C Standard (6.5.13 Logical AND operator)
4 Unlike the bitwise binary & operator, the && operator guarantees left-to-right evaluation; if the second operand is evaluated, there is a sequence point between the evaluations of the first and second operands. If the first operand compares equal to 0, the second operand is not evaluated.
and (6.5.14 Logical OR operator)
4 Unlike the bitwise | operator, the || operator guarantees left-to-right evaluation; if the second operand is evaluated, there is a sequence point between the evaluations of the first and second operands. If the first operand compares unequal to 0, the second operand is not evaluated.
The undefined behavior can occur only in the sub-expressions with the shift operator provided that these operands will be evaluated when values of the expressions --a
or a
are negative or too big. See the quote from the C Standard in the beginning of the answer.
Here is a correct demonstration program
#include <stdio.h>
int main( void )
{
int a = 1;
int b = 10;
int c = a && b << --a;
printf ( "a = %d, b = %d, c = %d\n", a, b, c );
c = a || b << a ;
printf ( "a = %d, b = %d, c = %d\n", a, b, c );
}
The program output is
a = 0, b = 10, c = 1
a = 1, b = 10, c = 1
In the both expressions
b << --a
and
b << a
the value of the right operand of the shift-left operator is equal to 0
. So the value of the expressions is equal to 10
because no shifting was done.