I am trying to implement a simple compiler using flex & bison, and got stuck in the postfix notation.
(The compiler should behave like the C++ compiler)
Here is the problem:
Given the following code:
int x = 0;
int y = x++ || x++ ; //y=1 , x = 2 this is understandable
int z = x++ + x++ ; // z = 0 , x=2
the first line is fine because of the following grammar:
expression = expression || expression; // x=0
expression = 0 || expression // x= 1
expression = 0 || 1 //x=2
expression = 1 // x=2
y = 1
However, I don't understand why z=0.
When my bison grammar sees 'variable' ++ it first returns the variables value, and only then increments it by 1. I used to think thats how C++ works, but it won't work for the 'z' variable.
Any advice on how to solve this case?
int z = x++ + x++;
Although z may appear to be 0, it is not, it could in fact be any value and will depend entirely upon the compiler you are using. This is because the assignment of z has undefined behaviour.
The undefined behaviour comes from the value of x being changed more than once between sequence points. In C++, the || operator is a sequence point which is why the assignment of y is working as expected, however the + operator is not a sequence point.
There are of course various other sequence points in C++, the ; being a more prominent example.
I should also point out that the ++ operator returns the previous value of the variable, that is in this example
#include <iostream>
using namespace std;
int main() {
int x = 0;
int y = x++;
cout << y << endl;
return 0;
}
The value printed out for y is 0.
Saying the same thing another way. A C compiler is free to implement
int z = x++ + x++;
as either
z = x + x
incr x
incr x
or as
int r1 = x;
incr x
z = r1 + x
incr x
Your compiler appears to be using the first plan.
Related
I am not able to understand, why the output of the code is not what I was expecting.
#include <iostream>
using namespace std;
int main()
{
int m = 2, n = 6;
int &x = m;
int &y = n;
m = x++;
x = m++;
n = y++;
y = n++;
cout<< m << " " << n;
return 0;
}
I was expecting 4 8
This line:
m = x++;
is equivalent to:
x = x++;
since m is a reference to x.
From c++17, the right hand side is evaluated first, resulting in 2. Then x is incremented to 3. Then the assignment of the right hand side value to the left hand side is done. But this uses the old value of the right hand side, which is 2. So the above statement effectively does nothing.
Before c++17,
m = x++;
is undefined behavior.
Notice how the operators are post-increment and not pre-increment... You're basically doing nothing.
m = x++; means that increment x (i.e. m) but return the old value of x (i.e. m). Assignment takes place after the increment and return of old value and the old value is what ends up getting assigned. So, you end up essentially with a bunch of self-assignments.
So in the following bit of code, I've been trying to figure out as to why the output of the code comes out to be...
X = 2 and Y = 2
when I originally thought it would be x = 1 and y = 1. I'm still a lil mind boggled with C++ coming into the semester and a little explanation with someone with more knowledge than me on this can hopefully mesh this concept into my head.
int main()
{
int x = 0;
int& y = x;
y++;
x++;
std::cout << "x = " << x << " y = " << y << std::endl;
}
x and y are not different than each other. Reference means the other name of x is y. So when you call y it calls x which means if you increase y it increases x. Then you increase x again and x becomes 2. And because y represents x, when you call y it calls x and you see 2 again.
key point is what the reference sign meant:
int& y = x;
It represent that you are assigning an alias to 'x', so 'y' is actually sharing the same memory address as 'x' do (physically).
doing
y++;
will change the value inside that memory address, which is shared by both 'x' && 'y'. Same as the operation
x++;
As a result, you did 2 increment on the same memory address with intial value of '0', which the value becomes '2'.
same idea, since both 'x' and 'y' are pointing to that exact same memory address, printing 'x' and 'y' will give you the same value.
Why this code produce 1? Someone, describe it for me pls.
#include <iostream>
using namespace std;
int main(){
int x = 0;
int y = 0;
if (x++&&y++){
y += 2;
}
cout << x + y << endl;
return 0;
}
Initially x and y are 0
Therefore x++ evaluates to false, and the second operand of && is never evaluated. x++ does increment x to 1. Since the condition is false, the conditional branch is not entered.
x + y is 1 + 0 which equals 1
user2079303 explains nicely (+1 by me already), as extension, I'll go a little more into detail:
if(x++) evaluates the value of x before the incrementation, so this little piece of code is equivalent to the following (need to buffer the old value!):
int tmp = x;
x++;
if(tmp)
Be aware that within c && cc, the second condition cc is not evaluated any more if c is already false! So if(x && y) is equivalent to
if(x)
{
if(y)
{
// ...
}
}
Putting all this together, your code is equivalent to this variant, where I separated the if clause into code lines each one containing only one single instruction:
int x = 0;
int y = 0;
int tmp = x;
x++;
if(tmp)
{
tmp = y;
y++;
if(tmp)
y += 2;
}
Suppose, your output now is quite obvious...
When I execute the following code
#include <iostream>
int & f(int &i)
{
puts("this is f");
return ++i;
}
int main()
{
int x = 5;
printf("%d", f(x) = f(x) + 1);
return 0;
}
I get output as 8. I am unable to understand how that's happening. Can anyone give me a reason for that?
It's unspecified which of the two calls to f(x) occurs first; and if the right-hand-side one was called first, it's unspecified whether the prvalue conversion of that occurs before or after the call to the left-hand-side's f(x). But all of those happen before the assignment.
One valid order is:
lhs f(x)
rhs f(x)
rvalue conversion
which results in i = 7 + 1;.
Alternatively it could be:
rhs f(x)
rvalue conversion
lhs f(x)
which results in i = 6 + 1;
There is no undefined behaviour relating to the ++ operator, because there is a sequence-point before and after each function call.
Refs:
[intro.execution]#15
Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced
[expr.ass]#1
In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.
In this quote, "the value computation of the assignment expression" means the value that this expression takes if it's a sub-expression of a larger expression, e.g. bar = ( foo(x) = foo(x) + 1 );
This code will let you inspect which order your compiler is using. I have inserted bar which passes its value through unmodified.
#include <cstdio>
using namespace std;
int & f(int idx, int &i)
{
printf("this is f %s\n", idx ? "right" : "left");
return ++i;
}
int bar(int idx, int z)
{
printf("bar%d = %d\n", idx, z);
return z;
}
int main()
{
int x=5;
f(0,x)= bar(0, bar(1, f(1,x)) + 1 );
printf("final = %d\n",x);
return 0;
}
Output for my system:
this is f left
this is f right
bar1 = 7
bar0 = 8
final = 8
Output for Coliru:
this is f right
bar1 = 6
bar0 = 7
this is f left
final = 7
Look how this line of code is evaluated:
printf("%d",f(x)=f(x)+1);
Step 1:
Initialization of x
--> x == 5
Step 2:
First call of f (right side of equals sign)
--> x == 6
Step 3:
Adding 1
--> x == 7
Step 4:
Second call of f (left side of equals sign)
--> x == 8
EDIT(see comment for deeper insight & thx to Matt):
You use reference parameter. When you modify the variable x in the f function, you modify too in main function.
First x = 5
When you call f first time, x = 6
When you call f second time, x = 7
Finally, 7 + 1 = 8
Function parameter i is a reference to int x.
So, ++i is actually incrementing x.
f(x) is called twice with initial x=5 which will make x equal to 7.
But 1 is added at printf, making final value printed 8.
#include <iostream>
int & f(int &i)
{
puts("this is f");
return ++i;
}
int main()
{
int x=5;
//f(x)=f(x)+1 equals to following
int& tmp_x = f(x); //x=6
int tmp = f(x)+1; //x = 7, tmp = 8
tmp_x = tmp; //x=8
printf("%d",tmp_x);
return 0;
}
I wrote the following function:
int divideBy2Power(int x, int y) { return (x >> y) + (x < 0 && x << (32 - y)); }
which is supposed to compute {x / (2^y)} (rounding towards zero) in an extremely efficient manner (i.e. without branching!)
In testing it works for most inputs, but for divideBy2Power(-2, 0) it produces -1. Likewise, x=-1, y=0 produces 0 (not -1). It works for bunches of other negative numbers.
I'm on a 32-bit machine and I checked that x << 32 produces zero.
Any ideas?
You have two sources of undefined behaviour (UB), and one of implementation-defined behaviour (IDB):
x << 32 is UB for all x (assuming you have 32-bit int on your platform).
-2 << y is UB for all y.
-2 >> y is IDB for all y.
So any behaviour you observe for divideBy2Power(-2, 0) is entirely down to "chance" (for lack of a better term).
I realise that this doesn't directly answer your question, but in a sense, it doesn't matter. Invoking UB twice in one expression should be avoided at all cost; you need to find a different way to write your function.
Ah, the answer is obvious once you break down the expression:
New code:
#include <stdio.h>
int divideBy2Power(int x, int y)
{
int a = x >> y;
int b = x < 0;
int c = x << (32-y);
printf("a=%d\n", a);
printf("b=%d\n", b);
printf("c=%d\n", c);
printf("b&&c=%d\n", (b&&c));
return a + (b && c);
}
int main()
{
printf("%d\n", divideBy2Power(-2, 0));
return 0;
}
Then you clearly see that b=1, c=-2 so b&&c = 1.
Your first problem is that you're adding the result of a binary AND comparison && (not bitwise); which will be either 0 or 1. That's nonsensical, it DOES use a branch, and has other questionable logic.
Secondly, see https://stackoverflow.com/a/9874464/1110687 for an explanation of why this fails with signed numbers and what exactly is undefined behavior in this case. It's about left-shifting but it applies to right-shifting also.
Perhaps you meant
int divideBy2Power(int x, int y) { return (x >> y) + ( (x < 0)& (x << (32 - y))); }
Note the extra parentheses. The order of precedence on << and >> is often not what you expect.