28

I've read that I should avoid the postfix increment operator because of performance reasons (in certain cases).

But doesn't this affect code readability? In my opinion:

for(int i = 0; i < 42; i++);
    /* i will never equal 42! */

Looks better than:

for(int i = 0; i < 42; ++i);
    /* i will never equal 42! */

But this is probably just out of habit. Admittedly, I haven't seen many use ++i.

Is the performance that bad to sacrifice readability, in this case? Or am I just blind, and ++i is more readable than i++?

15
  • 1
    I used i++ before I knew it could affect performance over ++i, so I switched. At first the latter did look a little strange, but after a little while I got used to it and now it feels as natural as i++.
    – gablin
    Commented Mar 19, 2011 at 9:41
  • 17
    ++i and i++ do different things in certain contexts, do not assume they are the same.
    – Orbling
    Commented Mar 19, 2011 at 11:32
  • 3
    Is this about C or C++? They are two very different languages! :-) In C++ the idiomatic for-loop is for (type i = 0; i != 42; ++i). Not only can operator++ be overloaded, but so can operator!= and operator<. Prefix increment is not more expensive than postfix, not-equal is not more expensive than less-than. Which ones should we use?
    – Bo Persson
    Commented Mar 19, 2011 at 20:39
  • 8
    Shouldn't it be called ++C?
    – Armand
    Commented Mar 21, 2011 at 11:05
  • 24
    @Stephen: C++ means take C, add to it, and then use the old one.
    – supercat
    Commented Mar 20, 2015 at 6:26

11 Answers 11

62

The facts:

  1. i++ and ++i are equally easy to read. You don't like one because you're not used to it, but there's essentially nothing you can misinterpret it as, so it's no more work to read or write.

  2. In at least some cases, the postfix operator will be less efficient.

  3. However, in 99.99% cases, it won't matter because (a) it'll be acting on a simple or primitive type anyway and it's only a problem if it's copying a big object (b) it won't be in a performance critical part of code (c) you don't know if the compiler will optimise it or not, it may do.

  4. Thus, I suggest using prefix unless you specifically need postfix is a good habit to get into, just because (a) it's a good habit to be precise with other things and (b) once in a blue moon you'll intend to use postfix and get it the wrong way round: if you always write what you mean, that's less likely. There is always a trade-off between performance and optimisation.

You should use you common sense and not micro-optimise until you need to, but neither be flagrantly inefficient for the sake of it. Typically this means: first, rule out any code construction which is unacceptably inefficient even in non-time-critical code (normally something representing a fundamental conceptual error, like passing 500MB objects by value for no reason); and second, of every other way of writing the code, choose the clearest.

However, here, I believe the answer is simple: I believe writing prefix unless you specifically need postfix is (a) very marginally clearer and (b) very marginally more likely to be more efficient, so you should always write that by default, but not worry about it if you forget.

Six months ago, I thought the same as you, that i++ was more natural, but it's purely what you're used to.

EDIT 1: Scott Meyers, in "More Effective C++" who I generally trust on this thing, says you should in general avoid using the postfix operator on user-defined types (because the only sane implementation of the postfix increment function is to make a copy of the object, call the prefix increment function to perform the increment, and return the copy, but copy operations can be expensive).

So, we don't know whether there are any general rules about (a) whether that is true today, (b) whether it also applies (less so) to intrinsic types (c) whether you should be using "++" on anything more than a lightweight iterator class ever. But for all the reasons I described above, it doesn't matter, do what I said before.

EDIT 2: This refers to general practice. If you think it DOES matter in some specific instance, then you should profile it and see. Profiling is easy and cheap and works. Deducing from first principles what needs to be optimized is hard and expensive and doesn't work.

4
  • Your posting is right on the money. In expressions where the infix + operator and post-increment ++ have been overloaded such as aClassInst = someOtherClassInst + yetAnotherClassInst++, the parser will generate code to perform the additive operation before generating the code to perform the post-increment operation, alleviating the need to create a temporary copy. The performance killer here is not post-increment. It is the use of an overloaded infix operator. Infix operators produce new instances. Commented Mar 22, 2011 at 17:24
  • 4
    I highly suspect that the reason people are 'used' to i++ rather than ++i is because of the name of a certain popular programming language referenced in this question/answer...
    – Shadow
    Commented Jul 30, 2018 at 7:02
  • My reason for using pre-increments everywhere (except in specific circumstances) is clarity. Everyone understands what pre-increment does and how it works. Post-increment is conceptually more complicated and people often get it subtly wrong. For instance, a common misconception is it means that x is incremented "after" the expression. So why use a more complicated concept when a simpler one does the job 99% of the time? Commented Feb 11, 2022 at 6:56
  • A language could recognize lvalue=value and lvalue++ as forms of statements rather than expressions; such treatment would be consistent with the way such expressions are typically used in C.
    – supercat
    Commented Nov 1, 2022 at 18:11
64

Always code for the programmer first and the computer second.

If there is a performance difference, after the compiler has cast its expert eye over your code, AND you can measure it AND it matters - then you can change it.

13
  • 7
    SUPERB statement!!!
    – Dave
    Commented Mar 19, 2011 at 20:27
  • 11
    @Martin: which is exactly why I'd use prefix increment. Postfix semantics imply keeping the old value around, and if there's no need for it, then it's inaccurate to use it. Commented Mar 20, 2011 at 19:36
  • 5
    @Matthew: It is simply not true that post-increment implies keeping a copy of the old value around. One cannot be sure how a compiler handles intermediate values until one views its output. If you take the time to view my annotated GCC generated assembly language listing, you will see that GCC generates the same machine code for both loops. This nonsense about favoring pre-increment over post-increment because it is more efficient is little more than conjecture. Commented Mar 21, 2011 at 13:37
  • 3
    @bit-twiddler: semantically, modern compilers optimize it away, fortunately, but it doesn't change the semantics of the operator. Commented Mar 21, 2011 at 17:16
  • 2
    @Mathhieu: The code that I posted was generated with optimization turned off. The C++ specification does not state that a compiler must produce a temporary instance of a value when post-incrementation is used. It merely states the precedence of the pre and post-increment operators. Commented Mar 21, 2011 at 17:47
14

GCC produces the same machine code for both loops.

C Code

int main(int argc, char** argv)
{
    for (int i = 0; i < 42; i++)
            printf("i = %d\n",i);

    for (int i = 0; i < 42; ++i)
        printf("i = %d\n",i);

    return 0;
}

Assembly Code (with my comments)

    cstring
LC0:
    .ascii "i = %d\12\0"
    .text
.globl _main
_main:
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %ebx
    subl    $36, %esp
    call    L9
"L00000000001$pb":
L9:
    popl    %ebx
    movl    $0, -16(%ebp)  // -16(%ebp) is "i" for the first loop 
    jmp L2
L3:
    movl    -16(%ebp), %eax   // move i for the first loop to the eax register 
    movl    %eax, 4(%esp)     // push i onto the stack
    leal    LC0-"L00000000001$pb"(%ebx), %eax // load the effective address of the format string into the eax register
    movl    %eax, (%esp)      // push the address of the format string onto the stack
    call    L_printf$stub    // call printf
    leal    -16(%ebp), %eax  // make the eax register point to i
    incl    (%eax)           // increment i
L2:
    cmpl    $41, -16(%ebp)  // compare i to the number 41
    jle L3              // jump to L3 if less than or equal to 41
    movl    $0, -12(%ebp)   // -12(%ebp) is "i" for the second loop  
    jmp L5
L6:
    movl    -12(%ebp), %eax   // move i for the second loop to the eax register 
    movl    %eax, 4(%esp)     // push i onto the stack
    leal    LC0-"L00000000001$pb"(%ebx), %eax // load the effective address of the format string into the eax register
    movl    %eax, (%esp)      // push the address of the format string onto the stack
    call    L_printf$stub     // call printf
    leal    -12(%ebp), %eax  // make eax point to i
    incl    (%eax)           // increment i
L5:
    cmpl    $41, -12(%ebp)   // compare i to 41 
    jle L6               // jump to L6 if less than or equal to 41
    movl    $0, %eax
    addl    $36, %esp
    popl    %ebx
    leave
    ret
    .section __IMPORT,__jump_table,symbol_stubs,self_modifying_code+pure_instructions,5
L_printf$stub:
    .indirect_symbol _printf
    hlt ; hlt ; hlt ; hlt ; hlt
    .subsections_via_symbols
4
  • How about with optimization turned on?
    – serv-inc
    Commented Feb 10, 2016 at 9:15
  • 2
    @user: Probably no change, but do you actually expect bit-twiddler to come back anytime soon? Commented Feb 10, 2016 at 19:26
  • 3
    Take care: While in C there are no user-defined types with overloaded operators, in C++ there are, and generalizing from basic types to user-defined types is simply invalid. Commented Feb 10, 2016 at 19:28
  • @Deduplicator: Thank you, also for pointing out that this answer does not generalize to user-defined types. I had not looked at his user page before asking.
    – serv-inc
    Commented Feb 11, 2016 at 9:06
12

Don't worry about performance, say 97% of the time. Premature Optimization is the Root of all Evil.

-- Donald Knuth

Now that this is out of our way, let's make our choice sanely:

  • ++i: prefix increment, increment the current value and yields the result
  • i++: postfix increment, copy the value, increment the current value, yields the copy

Unless a copy of the old value is required, using postfix increment is a round-about way of getting things done.

Inaccuracy comes from laziness, always use the construct that expresses your intent in the most direct way, there's less chance than the future maintainer might misunderstand your original intent.

Even though it's (really) minor here, there are times when I've been really puzzled by reading code: I was truly wondering whether the intent and the actual express coincided, and of course, after a few months, they (or I) didn't remember either...

So, it doesn't matter whether it looks right to you, or not. Embrace KISS. In a few months you'll have shun your old practices.

0
4

In C++, you could make a substantial performance difference if there are operator overloads involved, especially if you're writing templated code and don't know what iterators might be passed in. The logic behind any iterator X may be both substantial and significant- that is, slow, and unoptimizable by the compiler.

But this is not the case in C, where you know it will only be a trivial type, and the performance difference is trivial and the compiler can easily optimize away.

So a tip: You program in C, or in C++, and questions relate to one or the other, not both.

2

The performance of either operation is highly dependent on the underlying architecture. One has to increment a value that is stored in memory, which means that the von Neumann bottleneck is the limiting factor in both cases.

In the case of ++i, we have to

Fetch i from memory 
Increment i
Store i back to memory
Use i

In the case of i++, we have to

Fetch i from memory
Use i
Increment i
Store i back to memory

The ++ and -- operators trace their origin to the PDP-11 instruction set. The PDP-11 could perform automatic post-increment on a register. It could also perform automatic pre-decrement on an effective address contained in a register. In either case, the compiler could only take advantage of these machine-level operations if the variable in question was a "register" variable.

1

If you want to know if something's slow, test it. Take a BigInteger or equivalent, stick it in a similar for loop using both idioms, make sure the inside of the loop doesn't get optimized away, and time them both.

Having read the article, I don't find it very convincing, for three reasons. One, the compiler should be able to optimize around the creation of an object that is never used. Two, the i++ concept is idiomatic for numeric for loops, so the cases I can see actually being affected are limited to . Three, they provide a purely theoretical argument, with no numbers to back it up.

Based on reason #1 especially, my guess is that when you actually do the timing, they will be right next to each other.

-1

First of all it doesn't affect readability IMO. Its not what your used to seeing but it would only be a short while before you got accustomed to it.

Second unless you use a ton of postfix operators in your code you're likely not to see much of a difference. The main argument for not using them when possible is that a copy of the original var's value has to be kept until the end of the arguments where the original var could still be used. That's either 32bits or 64bits depending on the architecture. That equates to 4 or 8 bytes or 0.00390625 or 0.0078125 MB. Chances are very high that unless you're using a ton of them which need to be saved for a very long period of time that with today's computer resources and speed you wouldn't even notice a difference making a switch from postfix to prefix.

EDIT: Forget this remaining portion as my conclusion was proven false (except for the part of ++i and i++ not always doing the same thing... that's still true).

Also it was pointed out earlier that they don't do the same thing in a cases. Be careful about making the switch if you decide to. I've never tried it out (I've always used postfix) so I don't know for sure but I think changing from postfix to prefix will result in different results: (again I could be wrong... depends on the compiler/interpreter too)

for (int i=0; i < 10; i++) //the set of i values here will be {0,1,2,3,4,5,6,7,8,9}
for (int i=0; i < 10; ++i) //the set of i values here will be {1,2,3,4,5,6,7,8,9,10}
3
  • 4
    The increment operation occurs at the end of the for loop, so they would have the exact same output. It's not dependent on the compiler/interpreter.
    – jsternberg
    Commented Mar 19, 2011 at 16:24
  • @jsternberg... Thanks I wasn't sure when the increment happened as I never really had a reason to ever go test it out. Its been too long since I did compilers in college! lol
    – Kenneth
    Commented Mar 19, 2011 at 16:29
  • Wrong wrong wrong.
    – ruohola
    Commented May 12, 2019 at 19:31
-1

I think semantically, ++i makes more sense than i++, so I'd stick to the first one, except it's common to not do so (like in Java, where you should use i++ because it's widely used).

-2

It's not about only performance.

Sometimes you want to avoid implementing copying at all, because it doesn't make sense. And since usage of prefix increment does not depend on this it's plainly simpler to stick to prefix form.

And using different increments for primitive types and complex types... that's really unreadable.

-2

Unless you really need it, I'd stick to ++i. In most cases, this is what one intends. It's not very often you need i++, and you always have to think twice when reading such a construct. With ++i, it's easy: you add 1, use it, and then i is still the same.

So, I agree full-hearty with @martin beckett: make it easier for yourself, it's already hard enough.

Not the answer you're looking for? Browse other questions tagged or ask your own question.