245

The following for loops produce identical results even though one uses post increment and the other pre-increment.

Here is the code:

for(i=0; i<5; i++) {
    printf("%d", i);
}

for(i=0; i<5; ++i) {
    printf("%d", i);
}

I get the same output for both 'for' loops. Am I missing something?

nabster
  • 1,561
  • 2
  • 20
  • 32
theReverseFlick
  • 5,894
  • 8
  • 32
  • 33
  • I looked at several links but couldn't find the answer I was looking for. Short answer is "sequence points". Partially quoting C11 draft, Annex C Sequence Points: "— Between the evaluation of a full expression and the next full expression to be evaluated. The following are full expressions: ....each of the (optional) expressions of a for statement (6.8.5.3); the (optional) expression in a return statement (6.8.6.4).". This means that there is sequence point in for loop after every expression. So, it doesn't matter whether you do ++i or i++ or i+=1 or i=i+1 in the 3rd expression of for loop. – ZeZNiQ Feb 15 '20 at 00:36

12 Answers12

390

After evaluating i++ or ++i, the new value of i will be the same in both cases. The difference between pre- and post-increment is in the result of evaluating the expression itself.

++i increments i and evaluates to the new value of i.

i++ evaluates to the old value of i, and increments i.

The reason this doesn't matter in a for loop is that the flow of control works roughly like this:

  1. test the condition
  2. if it is false, terminate
  3. if it is true, execute the body
  4. execute the incrementation step

Because (1) and (4) are decoupled, either pre- or post-increment can be used.

DayTimeCoder
  • 4,294
  • 5
  • 38
  • 61
danben
  • 80,905
  • 18
  • 123
  • 145
  • 8
    When increment is called for a class, it might become non-equal to call post- or pre- increment, since it's behavior might differ much. – mbaitoff Jan 16 '11 at 15:48
  • So if there is no syntactical difference between `i++` and `++i`, why do I see people using `++i`? Is there *any* difference, even under the hood? – Andrew Feb 04 '11 at 22:49
  • 79
    Given that `i++` needs to remember the old value of `i` after incrementing, I think `++i` may be shorter (on the order of 1-2 instructions). – danben Feb 05 '11 at 00:18
  • 30
    Though the unused value should be optimized away, right? – Jamin Grey Jul 23 '13 at 20:34
  • 16
    @JaminGrey what does it hurt to be in the habit of ++i unless you have a reason for i++? – Azendale Jan 22 '15 at 04:04
  • 3
    @JaminGrey Optimized away, maybe. But once someone adds some more code that uses i, depending on the context and their inattention, subtle off by one bug introduced. Also, let's say you're using that operator on a class. Depending on the class, say construction has side effects, that won't be optimized away. – iheanyi Jun 02 '15 at 20:18
  • 14
    Azendale got it right. Defaulting to postinc/decrement is a very unfortunate habit that a lot of people have. Post should only be used if there's a clear and defensible reason for it. Sure, there probably won't be a difference for unevaluated primitives in any non-trivial compiler, but that doesn't mean post should be the default choice; such a habit is just asking for trouble, eventually. – underscore_d Aug 23 '15 at 13:52
  • Why does it matter that (2) and (4) are decoupled? I thought the only thing that mattered was whether the return value of the increment operation was used or not? – HelloGoodbye Sep 03 '15 at 22:38
  • 1
    I would argue the opposite to @underscore_d – omatai Mar 28 '17 at 00:59
  • 2
    @omatai That's funny, because people generally take 'arguing' in this sense to mean something backed up by reasoning/evidence. So, where is your reasoning/evidence for why postinc/decrement should be the preferred defaults? – underscore_d Mar 28 '17 at 11:36
  • Not sure where my reasoning/evidence went - it was there when I hit the button :-) There is an enormous amount of legacy code in which i++ exists in standalone statements and in for loops, where it makes no difference whatsoever. Should it all be converted? No - there is zero payback. So should new code be added in the old style or the new? Where there is no difference in performance, I would favour clarity & consistency, which usually means using i++ – omatai Apr 04 '17 at 01:14
  • @omatai I wasn't arguing for mass changing them and bogging down the VCS, etc. My point just that as a default, added to new code, it is suboptimal to me. IMO, prefix form is no less intuitive than postfix, and it is most semantically honest, i.e. doesn't involve (hopefully optimises away) a temp copy. Presumably you think postfix is better as it achieves parity with other operators in having the variable name first on the line? I guess I can see that, though it doesn't put me up nor down. Hence my own preference. – underscore_d May 02 '17 at 12:23
  • 27
    If C++ was called ++C, most probably the default habit would be ++i :) – marco May 07 '18 at 10:18
  • What happens if you just did for (int i=0; i++ < 10; )? Is it the same as ++i? – ATL_DEV Jan 15 '21 at 19:20
  • "for tab tab" in VS creates a for loop using i++. Maybe it will change if MS contracts azendale – Pedro77 May 20 '22 at 20:57
  • that is terrible, no reason to keep it though. i dont know why there would be losts of legacy code using i++ instead of ++i, their conceptual difference has existed since the beginning. that other people werent principled shouldnt be a reason for bad habits in yourself. – peter Sep 23 '22 at 03:00
130

Well, this is simple. The above for loops are semantically equivalent to

int i = 0;
while(i < 5) {
    printf("%d", i);
    i++;
}

and

int i = 0;
while(i < 5) {
    printf("%d", i);
    ++i;
}

Note that the lines i++; and ++i; have the same semantics FROM THE PERSPECTIVE OF THIS BLOCK OF CODE. They both have the same effect on the value of i (increment it by one) and therefore have the same effect on the behavior of these loops.

Note that there would be a difference if the loop was rewritten as

int i = 0;
int j = i;
while(j < 5) {
    printf("%d", i);
    j = ++i;
}

int i = 0;
int j = i;
while(j < 5) {
    printf("%d", i);
    j = i++;
}

This is because in first block of code j sees the value of i after the increment (i is incremented first, or pre-incremented, hence the name) and in the second block of code j sees the value of i before the increment.

jason
  • 236,483
  • 35
  • 423
  • 525
106

The result of your code will be the same. The reason is that the two incrementation operations can be seen as two distinct function calls. Both functions cause an incrementation of the variable, and only their return values are different. In this case, the return value is just thrown away, which means that there's no distinguishable difference in the output.

However, under the hood there's a difference: The post-incrementation i++ needs to create a temporary variable to store the original value of i, then performs the incrementation and returns the temporary variable. The pre-incrementation ++i doesn't create a temporary variable. Sure, any decent optimization setting should be able to optimize this away when the object is something simple like an int, but remember that the ++-operators are overloaded in more complicated classes like iterators. Since the two overloaded methods might have different operations (one might want to output "Hey, I'm pre-incremented!" to stdout for example) the compiler can't tell whether the methods are equivalent when the return value isn't used (basically because such a compiler would solve the unsolvable halting problem), it needs to use the more expensive post-incrementation version if you write myiterator++.

Three reasons why you should pre-increment:

  1. You won't have to think about whether the variable/object might have an overloaded post-incrementation method (for example in a template function) and treat it differently (or forget to treat it differently).
  2. Consistent code looks better.
  3. When someone asks you "Why do you pre-increment?" you'll get the chance to teach them about the halting problem and theoretical limits of compiler optimization. :)
Anders Sjöqvist
  • 3,372
  • 4
  • 21
  • 22
  • I think that you are wrong! The only difference between ++i and i++ is the order that you do things. In one case you would inc *i push *i and in the other you would push *i inc *i I would not concider this an optimization. – G. Allen Morris III Nov 27 '12 at 22:55
  • 20
    He's not wrong. Post-incrementing primitives when you don't use the copy can get optimized out quite easily by compilers. But the point he's alluding to in the last bit is that post-incrementing on iterators is implemented as a method call and the construction of an entirely new copy of the iterator. These can't be optimized out by the compiler because constructors and function calls can have side effects the compiler can't track and therefore can't assume aren't critical. – Jherico Feb 04 '14 at 21:17
  • > In one case you would inc *i push *i and in the other you would push *i inc *i I would not concider this an optimization. – MQDuck Oct 26 '15 at 11:02
  • 3
    He is wrong: I tried pre and post increment on compiler explorer here: https://godbolt.org/ and you get exactly the same assembly whichever way you put the increment. With C you're just describing what you want to happen to the compiler, if it amounts to the same thing then you usually get the same output, compilers are very clever these days. Pre and post increment almost never makes any difference - if you don't believe me try compiler explorer! – Will Jul 19 '17 at 08:01
  • Sure, but did you check it with the basic type or some more complex type which overloads the increment? – Karlovsky120 Oct 02 '18 at 20:06
  • he's not wrong, will obviously only read half ... – peter Sep 23 '22 at 03:06
32

This is one of my favorite interview questions. I'll explain the answer first, and then tell you why I like the question.

Solution:

The answer is that both snippets print the numbers from 0 to 4, inclusive. This is because a for() loop is generally equivalent to a while() loop:

for (INITIALIZER; CONDITION; OPERATION) {
    do_stuff();
}

Can be written:

INITIALIZER;
while(CONDITION) {
    do_stuff();
    OPERATION;
}

You can see that the OPERATION is always done at the bottom of the loop. In this form, it should be clear that i++ and ++i will have the same effect: they'll both increment i and ignore the result. The new value of i is not tested until the next iteration begins, at the top of the loop.


Edit: Thanks to Jason for pointing out that this for() to while() equivalence does not hold if the loop contains control statements (such as continue) that would prevent OPERATION from being executed in a while() loop. OPERATION is always executed just before the next iteration of a for() loop.


Why it's a Good Interview Question

First of all, it takes only a minute or two if a candidate tells the the correct answer immediately, so we can move right on to the next question.

But surprisingly (to me), many candidates tell me the loop with the post-increment will print the numbers from 0 to 4, and the pre-increment loop will print 0 to 5, or 1 to 5. They usually explain the difference between pre- and post-incrementing correctly, but they misunderstand the mechanics of the for() loop.

In that case, I ask them to rewrite the loop using while(), and this really gives me a good idea of their thought processes. And that's why I ask the question in the first place: I want to know how they approach a problem, and how they proceed when I cast doubt on the way their world works.

At this point, most candidates realize their error and find the correct answer. But I had one who insisted his original answer was right, then changed the way he translated the for() to the while(). It made for a fascinating interview, but we didn't make an offer!

Hope that helps!

Adam Liss
  • 47,594
  • 12
  • 108
  • 150
  • 27
    Well, you should reconsider asking this interview question because you made a grave but common mistake. A `for` loop can not be rewritten as you've specified in general. Look at `for(int i = 0; i < 42; i++) { printf("%d", i); continue; }` for example. Your claim is that it is semantically equivalent to `int i = 0; while(i < 42) { printf("%d", i); continue; i++; }` and that's clearly wrong. – jason Jan 16 '11 at 15:45
  • 9
    @Jason: And today I learned the edge case that I hadn't considered! In almost a decade, I've _never_ had a candidate recognize this. If I continue to use this question, which is valuable for the reasons I mentioned, I'll be sure to rephrase it per the edits above. And if a candidate offers the correct answer immediately, I'll ask if there are any exceptions. :-) +1 for your clear counter-example. Thank you! – Adam Liss Jan 16 '11 at 15:56
  • @Jason, am I missing something or doesn't your answer to the question state the same. @Adam Liss, I think you're right in your statement, see the complement to my answer. You can indeed also implement the continue quite easily. – jdehaan Jan 16 '11 at 16:22
  • @Adam Liss: Anyway, I think a nice interview question might be "rewrite an arbitrary `for` loop as a `while` loop." Almost surely you get your version. Ask if they are sure it's correct, and prod them by asking what happens if there is a `continue` in the body of the `for` loop and see if they can fix it. The reason I don't like your initial interview question is because their GPA in their CS classes will tell you whether or not they understand pre vs. post-increment. – jason Jan 16 '11 at 16:23
  • 1
    @jdehaan: I did not state that the general `for` loop is equivalent to the general `while` loop that you and @Adam Liss gave. I said in this particular instance the semantics between the OP's `for` loops and the `while` loops I gave are the same. In general, the naive translation from a `for` loop to a `while` loop is problematic and this how you got into trouble. As a tangential comment, note that the compiler doesn't need to even translate the OP's `for` loops into any kind of loop; the compiler could completely unroll the loop, for example. – jason Jan 16 '11 at 17:10
  • 1
    @Jason: Yes, I typically start with what looks like a simple interview question, and then "dig" a bit depending on the candidate's answer. Most of my experience until now has been with a tiny engineering company that tended to avoid new grads and favored EEs over developers who'd focused more on CS. (Not a judgment on my part, just the way it was.) In any case, I'm glad my answer led to this discussion and appreciate your thoughts. – Adam Liss Jan 16 '11 at 18:07
  • @AdamLiss, Your interview question demonstrates why the technical hazing process is counterproductive. You may have encountered a brilliant candidate who could have given you the "correct" answer, but realized the edge case and failed to articulate it under pressure. The candidate winds up appearing less competent than others who only saw the naive answer. The technical interview process is akin to a Turing test in trying to measure 'aptitude.' – ATL_DEV Jan 15 '21 at 20:01
  • @ATL_DEV if you think of it as an adversarial or hazing process, whether you're an interviewer or a candidate, then it is indeed counterproductive for the positions I've had to fill. On the other hand, if it's a collaborative and friendly conversation that reflects the actual work environment, you can each learn a lot about what it might be like to work together. Part of my job as an interviewer is to help the candidate relax and focus on the task, and to give them every opportunity to succeed. I'm trying to find out how much they can accomplish. I'm not trying to trip them up. – Adam Liss Jan 17 '21 at 02:03
  • @AdamLiss what's your definition of "equivalent"? i would not dare call them that, one pollutes the surrounding namespace, the other doesnt. you might want to add another pair of braces. – peter Sep 23 '22 at 03:15
9

Because in either case the increment is done after the body of the loop and thus doesn't affect any of the calculations of the loop. If the compiler is stupid, it might be slightly less efficient to use post-increment (because normally it needs to keep a copy of the pre value for later use), but I would expect any differences to be optimized away in this case.

It might be handy to think of how the for loop is implemented, essentially translated into a set of assignments, tests, and branch instructions. In pseudo-code the pre-increment would look like:

      set i = 0
test: if i >= 5 goto done
      call printf,"%d",i
      set i = i + 1
      goto test
done: nop

Post-increment would have at least another step, but it would be trivial to optimize away

      set i = 0
test: if i >= 5 goto done
      call printf,"%d",i
      set j = i   // store value of i for later increment
      set i = j + 1  // oops, we're incrementing right-away
      goto test
done: nop
tvanfosson
  • 524,688
  • 99
  • 697
  • 795
6

If you wrote it like this then it would matter :

for(i=0; i<5; i=j++) {
    printf("%d",i);
}

Would iterate once more than if written like this :

for(i=0; i<5; i=++j) {
    printf("%d",i);
}
TheCodeArtist
  • 21,479
  • 4
  • 69
  • 130
iss42
  • 2,720
  • 3
  • 21
  • 37
3

You could read Google answer for it here: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Preincrement_and_Predecrement

So, main point is, what no difference for simple object, but for iterators and other template objects you should use preincrement.

EDITED:

There are no difference because you use simple type, so no side effects, and post- or preincrements executed after loop body, so no impact on value in loop body.

You could check it with such a loop:

for (int i = 0; i < 5; cout << "we still not incremented here: " << i << endl, i++)
{
    cout << "inside loop body: " << i << endl;
}
Yola
  • 18,496
  • 11
  • 65
  • 106
3

Both i++ and ++i is executed after printf("%d", i) is executed at each time, so there's no difference.

Hoàng Long
  • 10,746
  • 20
  • 75
  • 124
2

The third statement in the for construct is only executed, but its evaluated value is discarded and not taken care of.
When the evaluated value is discarded, pre and post increment are equal.
They only differ if their value is taken.

Petruza
  • 11,744
  • 25
  • 84
  • 136
0

Yes, you'll get exactly same outputs for both. why do you think they should give you different outputs?

Post-increment or pre-increment matters in situations like this:

int j = ++i;
int k = i++;
f(i++);
g(++i);

where you provide some value, either by assigning or by passing an argument. You do neither in your for loops. It gets incremented only. Post- and pre- don't make sense there!

Nawaz
  • 353,942
  • 115
  • 666
  • 851
0

There is a difference if:

int main()
{
  for(int i(0); i<2; printf("i = post increment in loop %d\n", i++))
  {
    cout << "inside post incement = " << i << endl;
  }


  for(int i(0); i<2; printf("i = pre increment in loop %d\n",++i))
  {
    cout << "inside pre incement = " << i << endl;
  }

  return 0;
}

The result:

inside post incement = 0

i = post increment in loop 0

inside post incement = 1

i = post increment in loop 1

The second for loop:

inside pre incement = 0

i = pre increment in loop 1

inside pre incement = 1

i = pre increment in loop 2

finding83
  • 45
  • 3
  • 2
    This difference has nothing to do with the for loop; it's because you are using the pre/post increment when it has a side effect in the statement. – Foo Bah Jul 20 '11 at 18:45
-2

Compilers translate

for (a; b; c)
{
    ...
}

to

a;
while(b)
{
    ...
 end:
    c;
}

So in your case (post/pre- increment) it doesn't matter.

EDIT: continues are simply replaced by goto end;

jdehaan
  • 19,700
  • 6
  • 57
  • 97
  • Actually, that's not quite right. Look at `for(int i = 0; i < 42; i++) { printf("%d", i); continue; }` for example. Your claim is that is semantically equivalent to `int i = 0; while(i < 42) { printf("%d", i); continue; i++; }` and that's clearly wrong. – jason Jan 16 '11 at 15:44
  • See my edit. The statement is not from me but from a book I've read about compilers if I remember well it was this book http://www.amazon.com/Compilers-Principles-Techniques-Alfred-Aho/dp/0201100886. – jdehaan Jan 16 '11 at 16:17
  • 1
    I doubt a mistake like that is in the dragon book. Either way, perhaps a little more careful reading is in order? – jason Jan 16 '11 at 16:21