'precrement' and 'post-increment'

Started by
7 comments, last by Henners 20 years, 1 month ago
I read that the difference between c++ and ++c is that in c++, the variable is changed *after* it is used in the expression or line it was in, as opposed to ++c where the variable is changed *before* it is used. So how does this work with ''for'' loops? I was under the impression that there was a difference between:

    for (int i = 0; i <= 10; i++)
    {
        cout << "i: " << i << endl;
    }
    cout << "i: " << i << endl;
and

    for (int i = 0; i <= 10; ++i)
    {
        cout << "i: " << i << endl;
    }
    cout << "i: " << i << endl;
Yet both programs appear to produce the exact same results... Thanks for your time.
---------------------------------Use Firefox, you'll never ever look back.
Advertisement
Yes, both will have the exact same result in that code. The difference is noticed in code where the variable is actually used _and_ modified in the same expression.

E.G.

Index = 0;Val = Ary [Index++];  //  Val holds Ary [0], Index = 1Index = 0;Val = Ary [++Index];  //  Val holds Ary [1], Index = 1


lonesock

Piranha are people too.
You''ve got it right.

	int iValue;	iValue = 0;	if(iValue++ == 1)		std::cout << "POST" << std::endl; // Won''t Fire	iValue = 0;	if(++iValue == 1)		std::cout << "PRE" << std::endl; // Will Fire


The reason your loops produce the same result is that ++iLoop and iLoop++ are an expression in your for-loop, just like any other expression.

You''re telling it (as a single unit) add one to iLoop and it doesn''t do anything differently because no matter if its before or after, you''re ending up with iLoop + 1.

You can put any expression you want into the iterative condition of a for loop, as long as it affects the value.

By using a different expression, you can see how its really being affected.

Example:
	int iLoop = 0;	for(iLoop = 0; iLoop < 10; iLoop = (iLoop++ * 2))		std::cout << iLoop << std::endl; 

The output is: 0, 1, 3, 7

	int iLoop = 0;	for(iLoop = 0; iLoop < 10; iLoop = (++iLoop * 2))		std::cout << iLoop << std::endl; 

The output is: 0, 2, 6

As you can see, drastically different.

I think you''re just being hung up on the fact that its being evaluated as a single unit every iteration, just like any other statement.
In your for loop, you have three separate expressions, each evaluated at different times.

The first one, int i = 0 is evaluated before the first iteration.
The second one i <= 10 is evaluated before each iteration.
The third one i++ or ++i is evaluated after each iteration.

Since the second and third expressions are evaluated separately, and the value of i++ or ++i itself is never used (only its side effect), there is no difference in apparent behaviour in this case.

On the other hands, statements like: int j = i++; and int k = ++i; do yield different results. j will get the value of i before incrementation, and k will get the value of i after incrementation.

The reason why people advise to always use pre-incrementation (++i) is simple. When you use post-increment, the old value of i must be saved, then i is incremented, then the old value is returned. For basic types, and with today''s compilers, it doesn''t matter much, since the order of operations can simply be changed (use the value and only when you''re done, increment it).

But for user-defined types, such optimization cannot easily be done. You end up defining your operation like so:
Foo operator++(Foo& foo, int) // Postincrement (dummy ''int'' parameter){   Foo temp = foo;  // Make local copy of the old value.   ++foo.bar;       // Manipulate foo somehow.   return temp;     // Return temp by value.}Foo& operator++(Foo& foo) // Preincrement{   ++foo.bar;       // Manipulate foo somehow.   return foo;      // Return foo by reference.}


Depending on how complex Foo is, in particular the presence of a copy constructor, there may be no way to optimize the copying away from the post-increment function even if you declare the function inline, which leads to performance penalties. If you are not going to use the return value itself, there is therefore no point in using postincrement.

Now, you''ll ask me, why would you ever use a user-defined type in a for loop or similar? Well, all standard C++ containers (vector, deque, list...) provide iterator types for the specific purpose of walking (iterating) through their elements with such a loop. Incrementing the iterator means "move to the next element", which is done differently depending on the container: you don''t find the next element in the same way for a vector (contiguous storage, the next element *is* next in memory) or a list (linked list, has to follow a ''next'' pointer)...

So, iterator types need to define their own increment functions, which are not necessarily trivial. I think you''ll agree with me there is no point in calling a version that does extra work (back the old iterator up), if you''re not drawing any benefit from it.

Conclusion: use preincrement everywhere unless you have specific reasons not to. It takes the same number of keystrokes, is just as readable, will always give you at least as good performance as postincrement. And you''ll get used to it in no time.

“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.” — Brian W. Kernighan (C programming language co-inventor)
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
quote:Original post by GroZZleR
Example:
	int iLoop = 0;	for(iLoop = 0; iLoop < 10; iLoop = (iLoop++ * 2))		std::cout << iLoop << std::endl;  

The output is: 0, 1, 3, 7

	int iLoop = 0;	for(iLoop = 0; iLoop < 10; iLoop = (++iLoop * 2))		std::cout << iLoop << std::endl;  

The output is: 0, 2, 6


UNCLEAN! Both "iLoop = (iLoop++ * 2)" and "iLoop = (iLoop++ * 2)" are bad (in C, at least, I''m guessing it''s the same in C++, though). You''re modifying a variable twice without an intervening sequence point. Undefined behavior.

and to Fruny: I think i++ is the more common idiom in C and would therefore recommend it when using C and not C++. This has none of the drawbacks that it would in C++ (at least, it shouldn''t make a difference with any compiler worth its salt).
It makes sense now! And wow, those were really fast responses too.

Thank you so much, all of you
---------------------------------Use Firefox, you'll never ever look back.
quote:Original post by Fruny
Foo operator++(Foo& foo, int) // Postincrement (dummy ''int'' parameter){   Foo temp = foo;  // Make local copy of the old value.   ++foo.bar;       // Manipulate foo somehow.   return temp;     // Return temp by value.}Foo& operator++(Foo& foo) // Preincrement{   ++foo.bar;       // Manipulate foo somehow.   return foo;      // Return foo by reference.}



One small comment to add in addition to the excellent advice that you gave. In general, the postfix operator should be implemented in terms of the prefix operator (see Meyers, MEF C++).

Foo operator++(Foo& foo, int) // Postincrement (dummy ''int'' parameter){   Foo temp = foo;  // Make local copy of the old value.   ++foo;           // Invoke prefix ++ on original   return temp;     // Return temp by value.}Foo& operator++(Foo& foo) // Preincrement{   ++foo.bar;       // Manipulate foo somehow.   return foo;      // Return foo by reference.}


H

Keep in mind that the difference between prefix and postfix operators in the increment in the for loop can make a performance difference while using stl containers and iterators. If you use the postfix operator with these objects, an extra temporary object is created that could be eliminated with the prefix operator. Not a HUGE difference by any means, but if you have a for loop that will be excecuted many times and holds large objects, it can help out...
"What are you trying to tell me? That I can write an O(N^2) recursive solution for a 2-dimensional knapsack?" "No, programmer. I'm trying to tell you that when you're ready, you won't have to." -Adapted from "The Matrix"
Maybe what you don''t understand is what the ''for'' loop does and how expression evaluations works.

A ''for'' loop, is a shorcut for a while loop.

for(unsigned int i = 0; i < 10; i++){ std::cout << i << std::endl;} 


is the shorcut of:

unsigned int i=0;while(i<10){ std::cout << i << std::endl; i++;} 


about the evaluation, it''s more obvious the difference in asignments, checking this snippet:
unsigned int a, i = 0;a=++i;std::cout << a << std::endl; 

you can put the pre-increment evaluation as(the intrisics I actually don''t know):
i=i+1;a=i; 

while post=increment would be:
a=i;i=i+1; 

or in english:
pre-increment -> add 1 to i and then evaluates the expression.
post-increment -> evaluates the expression and then add 1 to i.

If you care about clear code, and don''t need over optimization, you wouldn''t need to care about post and pre increment, just use it in single expression.
----------------------------I would rather burn dollars than USA flags... but they are too expensive!

This topic is closed to new replies.

Advertisement