// Example program
#include <iostream>
#include <string>
int main()
{
int i=1;
int x;
x = ++i + ++i + ++i;
std::cout<< x;
return 0;
}
////////Why am I getting 10 and not 9 ?
/* if i starts at 1, increment operator takes precedence
so that means 2 3 4 then 2+3+4 = 9 not 10 ??
so how am I getting 10 ??
*/
AssertNull 1,094 Practically a Posting Shark
Rule 1 when experimenting with operator precedence. Do one thing at a time. You have five operations going on here. They clearly aren't happening in the order you think they are.
Change the line to this and see what happens:
x = i + ++i;
To really see what's going on, check out the disassembly in the debugger:
7 int i=1;
0x004013F6 movl $0x1,-0xc(%ebp)
8 int x;
9 x = ++i + ++i + ++i;
0x004013FD addl $0x1,-0xc(%ebp)
0x00401401 addl $0x1,-0xc(%ebp)
0x00401405 mov -0xc(%ebp),%eax
0x00401408 lea (%eax,%eax,1),%edx
0x0040140B addl $0x1,-0xc(%ebp)
0x0040140F mov -0xc(%ebp),%eax
0x00401412 add %edx,%eax
0x00401414 mov %eax,-0x10(%ebp)
10 std::cout<< x;
0x00401417 mov -0x10(%ebp),%eax
0x0040141A mov %eax,(%esp)
0x0040141D mov $0x6fcc43c0,%ecx
0x00401422 call 0x4014a4 <std::ostream::operator<<(int)>
0x00401427 sub $0x4,%esp
Okay, let's see what's up. Look at this part in particular.
0x004013FD addl $0x1,-0xc(%ebp)
0x00401401 addl $0x1,-0xc(%ebp)
Those are the first two things that happen. i gets incremented TWICE right off the bat. So i is 3 now from the first two ++'s.
NOW we add:
0x00401405 mov -0xc(%ebp),%eax
0x00401408 lea (%eax,%eax,1),%edx
3 + 3 is 6.
0x0040140B addl $0x1,-0xc(%ebp)
Now we increment i again. i is now 4.
0x0040140F mov -0xc(%ebp),%eax
0x00401412 add %edx,%eax
Now we add: 6 + 4 = 10.
See the order of precedence...
http://en.cppreference.com/w/cpp/language/operator_precedence
Now I DID get a warning at compile-time.
warning: operation on 'i' may be undefined [-Wsequence-point]
Not sure what that's about. It compiled exactly how I expected. Maybe it's not guaranteed? I had thought it was and it is in the order that it is in the link above. I'm actually confused on the warning since I thought the result would be unambiguously 10 on all compilers.
AssertNull 1,094 Practically a Posting Shark
Okay, I think I may understand what the ambiguity might be. If someone with more compiler knowledge and knowledge of the C++ spec can chime in, I'd appreciate it.
Take a line like this:
x = ++i + ++i;
The specification requires that both increments must take place before adding. However, it makes no stipulation about how the compiler handles the registers. My compiler used the lea
command. However, it could have used two separate registers (let's say eax and ebx):
- increment i: i = 2
- increment i: i = 3
- copy i(3) to eax register
- copy i(3) to ebx register
- add eax and ebx registers (3 + 3 = 6)
Result is 6.
The C++ standard requires that step 1 comes before step 3, step 2 comes before step 4, and steps 3 and 4 come before step 5. However it does NOT say that step 2 must come before step 3, so you could have this...
- increment i: i = 2
- copy i(2) to eax register
- increment i: i = 3
- copy i(3) to ebx register
- add eax and ebx registers (2 + 3 = 5)
Hence the compiler warning? Can anyone back this interpretation up or clarify? I do notice that if I change the OP's code to the following, I get no warnings and a result of 6.
// Example program
#include <iostream>
#include <string>
int main()
{
int i=1;
int j = 1;
int k = 1;
int x = ++i + ++j + ++k;
std::cout<< x;
return 0;
}
AssertNull 1,094 Practically a Posting Shark
Okay, one more post. According to this link:
http://en.cppreference.com/w/cpp/language/operator_incdec
if you go about halfway down, they have an example. Key l;ine of code is below:
// int n6 = n1 + ++n1; // undefined behavior
Here is the program.
#include <iostream>
int main()
{
int n1 = 1;
int n2 = ++n1;
int n3 = ++ ++n1;
int n4 = n1++;
// int n5 = n1++ ++; // error
// int n6 = n1 + ++n1; // undefined behavior
std::cout << "n1 = " << n1 << '\n'
<< "n2 = " << n2 << '\n'
<< "n3 = " << n3 << '\n'
<< "n4 = " << n4 << '\n';
}
The ++n1
must be executed before adding since the pre-increment takes precedence over addition. That is the ONLY thing that the spec sets in stone. The left addend and right addend must each be "evaluated" before adding them together, but there is nothing that says which side (left or right) must be "evaluated" first. Hence undefined behavior. The moral is that if you are incrementing the same scalar variable more than once in a statement (or any other operations with multiple "side-effects" on the same scalar variable in the same statement), it's better to break it into two or more statements in order to prevent undefined behavior.
CodyOebel commented: You are Brilliant :). So this is compiler dependent and all around bad programming practice. +3
CodyOebel -2 Junior Poster in Training
I am still struggling to find a logic here of predictability on the output, but maybe I need to let this one go, and toss it away as very bad programming example and it will result in inconsistent behavior. I dont like undefined behavior lol :) I think everything can be defined lol but maybe the catch 21 is this is defined as undefined a vicious loop of confusions lol.
Thinks AssertNull I could just see into your Brilliant mind trying to make a logic on this one too, and help another human being understand it. Your efforts and time spent are appreciated beyond words !
CodyOebel -2 Junior Poster in Training
Thanks oops not Thinks*
AssertNull 1,094 Practically a Posting Shark
You're welcome. Here is a good general approach when learning to code in C++. All languages must make decisions regarding trust/safety versus execution speed. The key to understanding the mindset of the folks who created/maintain/use C and C++ is that they defer to the programmers to know what they are doing and nothing must slow down execution speed. You have a small set of rules that you are expected to follow as a C/C++ coder. You are assumed to be responsible and smart enough to follow those rules and understand the consequences of not following those rules. That's why they are "warnings", not "errors". One accidental buffer overflow and you can completely wipe out the operating system. The compiler will hopefully warn you that something is undefined behavior and you are supposed to look at the code and see whether it needs fixing. If you decide that it is no big deal, the compiler shrugs and says "Oooookay, hope you know what you're doing". It does not feel any need to FORCE you to not ignore this warning or second guess you. This is a completely different approach from, say, Java, which forces you to "fix" or "handle" everything even if it isn't broken. For example, you could write and execute the following C++ code and wipe out your hard drive on accident.
#include <iostream>
#include <string>
using namespace std;
int main()
{
int a[2] = {0, 1};
int x = 5;
a[2] = 50000; // index out of bounds, wipes out x value
if(x == 50000)
{
cout << "Uh oh";
// execute code to wipe out my hard drive
}
return 0;
}
Java on the other hand does error-checking on the a[] array indexes at runtime and would throw an exception. Your hard-drive would be safe. Java doesn't trust you, so it checks for you. In fact, sometimes it finds problems that aren't even there and you have to "fix" them. Safety trumps execution speed in Java, unlike in C++.
Moral of the story is that there's a chunk of things that are defined as "undefined behavior" in C++. "Undefined behavior" means just that. Anything, absolutely anything goes. Compiler writers can handle that code however they like and they don't check at all to make sure it doesn't screw anything up. That's on you, the programmer. When you code with C++ and do something fancy, it's sometimes the equivalent of rewiring your house with the power on, no circuit breakers, no fuses. If you know what you're doing, you're fine. Maybe. The C++ compiler writer will almost always parse your code and turn it into machine code on the basis of optimization (speed, code size, memory usage, whatever) and that will vary depending on machine architecture and other things possibly outside of the programmer's control. It's even conceivable that the same compiler compiling on the same machine might compile a function differently depending on a small change somewhere else in the code. An example might be a function was called only once in the code. The compiler might optimize that function to be an inline function and an out-of-bounds array might not be a problem. A second function call might remove that inline optimization and the out-of-bounds array could corrupt the stack and crash the program or worse. Same C++ code, same mistake, but one time you get away with it and one time it's a disaster due to different optimizations by the same compiler on the same computer.
So, different results from different compilers. Best bet is to simply not do anything with undefined behavior, which means you sometimes need to break a statement into smaller statements. Your code (incrementing the same variable more than once in the same statement) was undefined behavior. Not only could one compiler print out 9 and another print out 10, but a third compiler could print out 12 million (highly doubtful, but it COULD).
Be a part of the DaniWeb community
We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.