I was just asked a question in a technical interview that I was a bit confused about.
The question was as follows:
If
int i = -1, int j = -1, and int k = -1,
and we run the following line:
++i && ++j && ++k
what would be the new values of i, j, and k? The reason I was confused is that, since we are not assigning this expression to anything, it doesn't seem like the and operators should make any difference (only the increment operators should). However, running a simple test program quickly proved that I was mistaken. Could someone explain this to me, as I have never seen this exercise before.
The key here is that && is short-circuiting.
So, ++i is evaluated first. It increments i and returns the new value, which is 0, so the rest of the expression doesn't get evaluated.
The values should be 0, -1, -1 if I'm not mistaken.
The value of the expression ++i is 0 in this case, which is to say false so the and operation shortcuts and the latter expressions are never evaluated.
Related
I came through this code snippet while I was referring to C++ multiple-choice questions. Previously I used only && or || for combining multiple conditions, but this code uses ',':
using namespace std;
int main()
{
int i;
for (i = 0; i < 0, 5; i++)
printf("%d ", i);
return 0;
}
Formally, the answer for this one is an infinite loop. What is the working of this snippet? How are the conditions getting evaluated?
You have a comma operator in the condition of your for loop. The comma operator evaluates its first operand and discards the result, and then evaluates the second operand and returns this value (and type). The comma operator also has the lowest precedence of all C/C++ operators, meaning it's always the last one to bind to an expression.
So the condition in your for loop is equivalent to:
(i < 0), 5
The result of this expression is always 5, which is not 0 (false). Hence the condition is always true.
If you leave loop running for long enough eventually i, which is a signed integer, will overflow. This results in undefined behaviour (thanks #Jarod42).
In function 'int main()':
10:16: warning: left operand of comma operator has no effect [-Wunused-value]
Run your code here and read the warnings.
int x = 5,y = 10;
bool boolean = 0;
int k = (boolean ? ++x, ++y : --x, --y);
cout<<k;
When boolean is 0,it outputs 9, however when it is 1 it outputs 10.I know this is happening because of precedence but cannot exactly figure out how is it happening, please help me understand this.
NOTE:I know I can get the expected output if I use parenthesis,or better write a clean code,I am just using this to understand how compiler would evaluate expressions like these according to precedence.
, has lower precedence than ?:. Which means that the full parenthesising is:
int k = ((boolean ? (++x, ++y) : --x), --y);
As you can see, k is always initialised to the value of --y. It's just that if boolean is true, ++y happens before that.
When looking for the full parenthesis form of an expression, think of it as constructing the expression tree (where the lowest-precedence operator is at the root).
Find the lowest-precedence operator in an expression, and parenthesise its left-hand side argument and its right-hand side argument. Repeat recursively within the sub-expressions just parenthesised.
Due to the comma operator having the lowest operator precedence, your statement is actually equal to
k = (boolean ? (++x, ++y) : --x), --y;
That means when boolean is true you both increase and decrease y. The result of the ternary expression is thrown away in both cases and k is only assigned the result of --y.
It should be noted that this is not undefined behavior, as the comma operator introduces a sequence point.
To get the result you expect, you need to do
k = boolean ? (++x, ++y) : (--x, --y);
Note that the parentheses around ++x, ++y is strictly not needed, but it does make the expression clearer.
Given the above excellent answers, one should write instead:
if (boolean) {
++x;
++y;
} else {
--x;
--y;
}
int k = y;
Because then the code is more readable and clear in its intent. This will help anyone who has to maintain the code (including the original author!) without anyone having to waste time by asking SO questions or worrying about the precedence of , or ?: or what the logistics of assignment to such a complex expression are. Any modern compiler will optimize both this and the above to the same resulting code
I've been familiar with the ternary operator for quite some time now, and have worked with it in a few differnet languages. My understanding of the operator is this:
condition ? expr1 : expr2
However, in C++, the following code is legal:
int i = 45;
(i > 0) ? i-- : 1;
Aren't you, in effect, just writing 1; or i - 1;How is this a complete statement? I understand that the intention of the code is to decrement i if it's greater than 0, but I would've thought that the code would generate a compiler error as just being an expression, not a full statement. I expected code like this:
int i = 45;
i = (i > 0) ? i - 1 : i;
This is called expression statement. The expression is evaluated and its value is discarded.
Even this is valid:
42;
although it does nothing. Only side effects (like i--, assignment, etc) in the expression have effects.
In fact, many statements we use are expression statements: assignments, function calls, etc:
a = 42;
foo();
That is a valid expression. You might have received a warning because you are not saving the result of the expression, but that you have the i-- your statement does have an effect.
In C++, an expression like 1 is a perfectly valid statement with no side effects. You could very feasibly write this function:
void f() {
1;
}
In fact, even this is correct.
void f() {
;;;;
}
A literal statement evaluates its arguments but does nothing more. The system views 1; as being just like func();. The only difference is that while func(); would logically have some side effects, 1; does not so it ends up being a no-op. The ternary operator evaluates like an if-statement, so the second form is only evaluated if the operand is true. Thus:
(i > 0) ? i-- : 1;
If i is greater than 0, the second form is evaluated. When it is evaluated, it carries its side effect, which decrements i by 1. Otherwise, the third form is evaluated, which does nothing. Although this block of code works, it is not incredibly readable, so while it's nice toy code a real if-statement is ideal for situations like this. For the same reason, this line would have the same effect but be frowned upon for being equally unreadable.
((i > 0) && (i--)) || 1;
Assuming you didn't overwrite the boolean operators, this code will short-circuit and behave like the ternary operator. If i is not greater than 0, then the && need not evaluate its second operand since the && is false, but the || must since it might be true. Inversely, if i is greater than 0, the && needs to evaluate but the || already knows it's true.
Aren't you, in effect, just writing 1; or i - 1;
No: i-- is not the same as i - 1. In the first case, the value of i is modified. In the second case it is not.
In the event that i less than or equal to zero, then you're correct that the resulting 'code' will be 1. However, the compiler will realise that this is not a useful thing to execute and so it ought to generate code equivalent to:
if( i > 0 ) i--;
Some (including myself) would consider that using the ternary operator in this fashion is bad style. Just for fun, here's another way someone might write it that's also not very nice (also more likely to generate compiler warning):
i > 0 && i--;
In the end, style is a matter of preference. The compiler, for the most part, will decide the best way to turn your code into assembly. So you owe it to yourself to write code that is clear and concise.
What is the output of the following code:
int main() {
int k = (k = 2) + (k = 3) + (k = 5);
printf("%d", k);
}
It does not give any error, why? I think it should give error because the assignment operations are on the same line as the definition of k.
What I mean is int i = i; cannot compile.
But it compiles. Why? What will be the output and why?
int i = i compiles because 3.3.1/1 (C++03) says
The point of declaration for a name is immediately after its complete declarator and before its initializer
So i is initialized with its own indeterminate value.
However the code invokes Undefined Behaviour because k is being modified more than once between two sequence points. Read this FAQ on Undefined Behaviour and Sequence Points
int i = i; first defines the variable and then assigns a value to it. In C you can read from an uninitialized variable. It's never a good idea, and some compilers will issue a warning message, but it's possible.
And in C, assignments are also expressions. The output will be "10", or it would be if you had a 'k' there, instead of an 'a'.
Wow, I got 11 too. I think k is getting assigned to 3 twice and then once to 5 for the addition. Making it just int k = (k=2)+(k=3) yields 6, and int k = (k=2)+(k=4) yields 8, while int k = (k=2)+(k=4)+(k=5) gives 13. int k = (k=2)+(k=4)+(k=5)+(k=6) gives 19 (4+4+5+6).
My guess? The addition is done left to right. The first two (k=x) expressions are added, and the result is stored in a register or on the stack. However, since it is k+k for this expression, both values being added are whatever k currently is, which is the second expression because it is evaluated after the other (overriding its assignment to k). However, after this initial add, the result is stored elsewhere, so is now safe from tampering (changing k will not affect it). Moving from left to right, each successive addition reassigns k (not affected the running sum), and adds k to the running sum.
I recently learned about the , operator and the fact that it introduces a sequence point.
I also learned that the following code led to undefined behavior:
i = ++i;
Because i was modified twice between two sequence points.
But what about the following codes ?
i = 0, ++i;
i = (0, ++i);
While I know the rules, I can't get to a conclusion. So is it defined behavior or not ?
edit: Just as #paxdiablo mentions, defined or not, this is really a bad practice which should be avoided. This question is asked solely for educational purposes and better understanding of the "rules".
Yes. = has higher precedence than ,, so this expression is equivalent to (i = 0), ++i. , is a sequence point, so it's guaranteed that the ++i occurs after the assignment.
I'm not sure whether i = (0, ++i) is defined though. My guess would be no; there's no sequence point between the increment and the assignment.
i = 0, ++i;
As the other answer pointed out it is not Undefined Behaviour.
i = (0, ++i);
The behaviour is undefined in this case because there is no sequence point between ++i and assignment to i.
i = (0, ++i, 0)
The behaviour is well defined1 in C++03, IMHO.
1 See extended discussion for a similar expression.