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.
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).
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.
Related
This question already has answers here:
Is short-circuiting logical operators mandated? And evaluation order?
(7 answers)
Closed 2 years ago.
#include <stdio.h>
int main()
{
int a = 1;
int b = 1;
int c = a || --b;
int d = a-- && --b;
printf("a=%d, b= %d, c= %d, d= %d",a,b,c,d);
return 0;
}
In th above code, I expected output to be a=0, b= -1, c= 1, d= 0 but the output was a=0, b= 0, c= 1, d= 0
Screenshot_VS Code
In the expression used as an initializer in this declaration
int c = a || --b;
as the operand a is not equal to 0 then the expression --b is not evaluated.
So the variable c is initialized by 1.
From the C Standard (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.
In the expression used as an initializer in tjis declaration
int d = a-- && --b;
the operand a-- is not equal to 0 (the value of the postfix operator is the value of its operand before decrementing). So the operand --b is evaluated.
As its value is equal to 0 then the variable d is initialized by 0.
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.
As a result a and b will be equal 0 after this declaration.
Hi just wondering if you use a chained assigment in an if condition, would the leftmost variable be used to check the if condition
like a=b=c , its a thats ultimetly checked and not b or c
#include <stdio.h>
int main()
{
int a, b, c =0;
// does this reduce to a == 100 and the variables b or c are not checked if they are == to 100 but simply assigned the value of 100 ?
if( (a = b = c = 100) == 100)
printf( "a is 100 \n");
return 0;
}
The expression is not actually checking a or b or c.
An assignment expression, like any expression, has a value. And in this case it is the value that is stored. However, the actual storing of the value in an object is a side effect so there's no guarantee that it has happened at the time the comparison operator is evaluated.
So the condition is actually more like:
if (100 == 100)
With the assignment to a, b, and c happening in a manner that is unsequenced with respect to the comparison.
This is spelled out in section 6.5.16p3 of the C standard regarding assignment operators:
An assignment operator stores a value in the object designated by the left operand. An assignment expression has the value of the left operand after the assignment, but is not an lvalue. The type of an assignment expression is the type the left operand would have after lvalue conversion. The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands. The evaluations of the operands are unsequenced.
The condition is always true. Your code is equivalent to:
a = 100;
b = 100;
c = 100;
printf( "a is 100 \n");
#include <stdio.h>
int main() {
int a = 1;
int b = a || (a | a) && a++;
printf("%d %d\n", a, b);
return 0;
}
when I ran this code the results were 1 and 1.
According to the C language Operator Precedence the operation && is supposed to happen before the operation ||. So shouldn't the result be 2 1 ? (a = 2, b = 1)
when OR'ing expressions in C, a shortcut is taken, I.E. as soon as an expression is evaluated to TRUE, the rest of the OR'd expressions are not evaluated
The first expression a evaluates to TRUE, so all the rest of the expressions are not evaluated, so a is never incremented
When applying the operator precedence rules, the expression is equivalent to:
int b = a || ((a | a) && a++);
The evaluation or the operands to || and && is performed from left to right and shortcut evaluation prevents evaluating the right operand if the left operand can determine the result: since a is non zero, a || anything evaluates to 1 without evaluating the right operand, hence bypassing the a++ side effect.
Therefore both a and b have value 1 and the program prints 1 1.
Conversely, if you had written int b = (a || ((a | a)) && a++;, the left operand of && would have value 1, so the right operand need to be evaluated. a++ evaluates to 1 but increments a, so b have final value 1 and a is set to 2, producing your expected result.
The confusion comes from equating operator precedence with order of evaluation. These are two separate notions: operator precedence determines the order in which to apply the operators, but does not determine the order of evaluation of their operands.
Only four operators have a specified order of evaluation of their operands: &&, ||, ? : and , and the first 2 may skip evaluation of one, depending on the value of the other operand and the third only evaluates the first and only one among the second and third operands. For other operators, the order of evaluation of the operands is unspecified, and it may differ from one compiler to another, one expression to another or even one run to another, although unlikely.
When I try to put a || (a | a) into a parenthesis. The result was as you expected.
So I guess that when C compiler executes an OR operator and it get a True value to the OR, the execution will finish immediately.
In your case, when the compiler executed the a || (a | a) (1 || something) operation. Value of b will be declared to 1 right away and a++ operator won't be execute.
I'm studying C from A Book on C by Kelley-Pohl, and there's this exercise that I don't understand:
int a = 0, b = 0, x;
x = 0 && (a = b = 777);
printf("%d %d %d\n", a, b, x);
x = 777 || (a = ++b);
printf("%d %d %d\n", a, b, x);
They just say to imagine the output and compare it to the real one. I thought the output would have been
777 777 0
778 778 1
but it is
0 0 0
0 0 1
From the C Standard (6.5.13 Logical AND operator)
3 The && operator shall yield 1 if both of its operands compare
unequal to 0; otherwise, it yields 0. The result has type int.
and
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.
In this expression statement
x = 0 && (a = b = 777);
the first operand compares equal to 0. So the second operand is not evaluated that is the values of the variables a and b are not changed. So the variable x will be set to 0 according to the paragraph #3 of the section.
From the C Standard (6.5.14 Logical OR operator)
3 The || operator shall yield 1 if either of its operands compare
unequal to 0; otherwise, it yields 0. The result has type int.
and
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.
In this expression statement
x = 777 || (a = ++b);
the first operand compares unequal to 0. So the second operand is not evaluated that is the values of the variables a and b are not changed.. So the variable x will be set to 1 according to the paragraph #3 of the section.
If you will change the order of the operands in the expressions like
x = (a = b = 777) && 0;
x = (a = ++b) || 777;
you get the expected by you result.
The && operator uses lazy evaluation. If either side of the && operator is false, then the whole expression is false.
C checks the truth value of the left hand side of the operator, which in your case is 0. Since 0 is false in c, then the right hand side expression of the operation, (a = b = 777), is never evaluated.
The second case is similar, except that || returns true if the left hand side expression returns true. Also remember that in c, anything that is not 0 is considered true.
Hope this helps.
Another trap in this expression is that; the precendence of the operators. Such as &&, || (logical and, logical or) operators have higher precedence to the assignment operator(=).
in this case x=(0&&(a=b=777)) is same as x=0&&(a=b=777), however x=(0&(a=b=777)) is more readable than the previous one.
Logical operators select one of their operands and returns the result accordingly.
They also force their operands to be boolean as true or false.
In this expression "x=0&&(a=b=777)" since the first operand is false the result will be equal to first operand.Second operand is short circuited and will not be executed.So the output will be a=b=0, x=0.
x=777 || (a=++b) in this expression since the first operand is true the result will be equal to the first operand and logical operator will not check the second operand, logical OR operator will bypass the second operand.In this expression since the first operand is true (777 is converted to true) the result will be True means x=1.Since the second operand is skipped "a" and "b" values will remain same as their previous values, in this case 0,0
For the example, after I use
int a = 5, b = 6;
x = (a < b) ? a++ : b++;
x gets the value of a, which is 5, and a increments to 6, which is expected.
When I use
a = (a < b) ? a++ : b++;
After this line, a still remains 5.
But
a = (a++ < b++) ? a : b;
a is now 6.
Why is this happening and why isn't increment operator executed in the first case?
EDIT: Just to clarify, I'm asking why this happens when I'm using these lines separately, one by one, not all three in the same time.
a = (a < b) ? a++ : b++;
here, we stored a in a, and then incremented it. But it is like
a = a++; // as a<b
which shows undefined behaviour.
a = (a++ < b++) ? a : b;
here, a is being incremented at the time of comparison, so now a is 6, which is stored in a.
Both cases involve undefined behaviour of some sort because you are incrementing a, returning a and assigning to a on the left side within 2 sequence points. 3 would be required for a clearly defined result.
In this case :
a = (a < b) ? a++ : b++;
if a is smaller than b
a is returned (value = 5) as the result of the ternary operator
a is incremented (value = 6).
the result of the ternary operator (5) is assigned to the left hand side variable a (over-writing 6)
The order of steps 3 and 4 is not defined. It is equivalent to a = a++;
In this case :
a = (a++ < b++) ? a : b;
If a is smaller that b
a and b are incremented (regardless which is smaller)
a is returned as the result of the ternary operator
it is assigned to the left hand side variable a
The order of steps 2 and 3 is not clearly defined.
It's important to keep track of sequence points in such cases. Relevant rules :
The 1st expression of ternary operator on the left of ? is sequenced before the 2nd or 3rd expressions. And either of them is sequenced before assignment.
The comparison is sequenced before the ?
In expressions like a++ the value is returned before incrementing
Undefined behaviour:
In expressions like a = a++; there is no sequence point between (post)incrementing a and assigning to a on the left side. Both happen after the original value of a is returned
In expressions like a++ ? a : b there is no sequence point between (post)incrementing a and returning a from the ternary operator. Both happen after the ?
The one line that gives you the unexpected result has an error: you are not allow to modify an object (here a) twice in the same expression without separating them by a "sequence point".
The ? in the other one is a sequence point, so that one works.
If you do so (modify twice without sequence point) the behavior of your program becomes undefined. That is you can't make any reasonable assumption of what the program should produce and thus you see some unexpected results that even will vary according to the compiler and its version.
From the horse's mouth:
6.5 Expressions
...
2 If a side effect on a scalar object is unsequenced relative to either a different side effect
on the same scalar object or a value computation using the value of the same scalar
object, the behavior is undefined. If there are multiple allowable orderings of the
subexpressions of an expression, the behavior is undefined if such an unsequenced side
effect occurs in any of the orderings.84)
84) This paragraph renders undefined statement expressions such as i = ++i + 1;
a[i++] = i;
while allowing i = i + 1;
a[i] = i;
6.5.15 Conditional operator
...
4 The first operand is evaluated; there is a sequence point between its evaluation and the
evaluation of the second or third operand (whichever is evaluated). The second operand
is evaluated only if the first compares unequal to 0; the third operand is evaluated only if
the first compares equal to 0; the result is the value of the second or third operand
(whichever is evaluated), converted to the type described below.110)
110) A conditional expression does not yield an lvalue.
6.5.16 Assignment operators
...
3 An assignment operator stores a value in the object designated by the left operand. An
assignment expression has the value of the left operand after the assignment,111) but is not
an lvalue. The type of an assignment expression is the type the left operand would have
after lvalue conversion. The side effect of updating the stored value of the left operand is
sequenced after the value computations of the left and right operands. The evaluations of
the operands are unsequenced.
111) The implementation is permitted to read the object to determine the value but is not required to, even
when the object has volatile-qualified type.
In the expression
a = (a < b ) ? a++ : b++
there is a sequence point between the evaluation of (a < b) and a++, but there is no sequence point between the evaluation of a on the LHS of the = operator and a++; thus, the behavior is undefined.
In the expression
a = (a++ < b++) ? a : b
there is a sequence point between (a++ < b++) and the a on the RHS of the ? operator, but there's no sequence point between the a on the LHS of the = operator and (a++ < b++); again, the behavior is undefined.