Search

5.4 — Increment/decrement operators, and side effects

Incrementing and decrementing variables

Incrementing (adding 1 to) and decrementing (subtracting 1 from) a variable is so common that they have their own operators.

Operator Symbol Form Operation
Prefix increment (pre-increment) ++ ++x Increment x, then return x
Prefix decrement (pre-decrement) –– ––x Decrement x, then return x
Postfix increment (post-increment) ++ x++ Copy x, then increment x, then return the copy
Postfix decrement (post-decrement) –– x–– Copy x, then decrement x, then return the copy

Note that there are two versions of each operator -- a prefix version (where the operator comes before the operand) and a postfix version (where the operator comes after the operand).

The prefix increment/decrement operators are very straightforward. First, the operand is incremented or decremented, and then expression evaluates to the value of the operand. For example:

This prints:

6 6

The postfix increment/decrement operators are trickier. First, a copy of the operand is made. Then the operand (not the copy) is incremented or decremented. Finally, the copy (not the original) is evaluated. For example:

This prints:

6 5

Let’s examine how this line 6 works in more detail. First, a temporary copy of x is made that starts with the same value as x (5). Then the actual x is incremented from 5 to 6. Then the copy of x (which still has value 5) is returned and assigned to y. Then the temporary copy is discarded.

Consequently, y ends up with the value of 5 (the pre-incremented value), and x ends up with the value 6 (the post-incremented value).

Note that the postfix version takes a lot more steps, and thus may not be as performant as the prefix version.

Here is another example showing the difference between the prefix and postfix versions:

This produces the output:

5 5
6 4
6 4
6 4
7 3

On the 8th line, we do a prefix increment and decrement. On this line, x and y are incremented/decremented before their values are sent to std::cout, so we see their updated values reflected by std::cout.

On the 10th line, we do a postfix increment and decrement. On this line, the copy of x and y (with the pre-incremented and pre-decremented values) are what is sent to std::cout, so we don’t see the increment and decrement reflected here. Those changes don’t show up until the next line, when x and y are evaluated again.

Best practice

Strongly favor the prefix version of the increment and decrement operators, as they are generally more performant, and you’re less likely to run into strange issues with them.

Side effects

A function or expression is said to have a side effect if it does anything that persists beyond the life of the function or expression itself.

Common examples of side effects include changing the value of objects, doing input or output, or updating a graphical user interface (e.g. enabling or disabling a button).

Most of the time, side effects are useful:

The assignment operator in the above example has the side effect of changing the value of x permanently. Even after the statement has finished executing, x will still have the value 5. Similarly with operator++, the value of x is altered even after the statement has finished evaluating. The outputting of x also has the side effect of modifying the state of the console, as you can now see the value of x printed to the console.

However, side effects can also lead to unexpected results:

C++ does not define the order in which function arguments are evaluated. If the left argument is evaluated first, this becomes a call to add(5, 6), which equals 11. If the right argument is evaluated first, this becomes a call to add(6, 6), which equals 12! Note that this is only a problem because one of the arguments to function add() has a side effect.

There are other cases where C++ does not specify the order in which certain things are evaluated (such as operator operands), so different compilers may exhibit different behaviors. Even when C++ does make it clear how things should be evaluated, historically this has been an area where there have been many compiler bugs. These problems can generally all be avoided by ensuring that any variable that has a side-effect applied is used no more than once in a given statement.

Warning

C++ does not define the order of evaluation for function arguments or operator operands.

Best practice

Don’t use a variable that has a side effect applied to it more than once in a given statement. If you do, the result may be undefined.


5.5 -- Comma and conditional operators
Index
5.3 -- Modulus and Exponentiation

221 comments to 5.4 — Increment/decrement operators, and side effects

  • Waldo Lemmer

    > Incrementing (adding 1 to) and decrementing (subtracting 1 from) a variable are so common that they have their own operators.

    "are" should be "is".

    I learned a lot in this lesson, thanks!

  • litaci

    how would something like this evaluate?

    • sapphirespire

      left to right, addition before subtraction

      ++x returns 6

      --y returns 4, y++ returns 4 and changes y to 5
      (--y + y++) == 4 + 4 == 8

      x-- returns 6 and changes x to 5, ++y returns 6
      (x-- + ++y) == 6 + 6 == 12

      6 - 8 == -2
      -2 - 12 == -14

      x == 5, y == 6, z == -14

      • sapphirespire

        In visual studio the affixes are processed first, from left to right, then operators are applied from left to right without grouping. So it's:

        6 - 4 == 2
        plus 4 == 6
        - 6 == 0
        plus 5 == 5

        x == 5, y == 6, z == 5

    • nascardriver

      It causes undefined behavior, because you have multiple unsequenced side affects applying to a single object (`x` and `y`).
      @saphhirrespire's reply is a common misconception.

  • TupleMan

    I suppose it's a mistake but the 3rd example : "This produces the output:" actually does not show any output, it's totally blank.

  • Regarding postfix increment/decrement; how often does this involve in a programming?  It seems like it can really mess up your code when use.

  • Andreas Krug

    Small suggestion for third code example (lines 7-11):
    std::cout << ... << ' ' << ... << '\n'; instead of std::cout << ... << " " << ... << '\n';

  • sam

    "Don’t use a variable that has a side effect applied to it more than once in a given statement. If you do, the result may be undefined."

    in the the following example :
    int x {5};
    int y = ++x - x++;

    the "x++" has post-increament should evaluate first it has higher precedence than "++x"
    so the expression should evaluate : y = 7  - 5

    why the compiler doesn't evaluate x++ first then ++x ?

    bad english sorry :(

    • nascardriver

      The increments to `x` are unrelated to each other in this case, so precedence doesn't affect them. This is the same as

      It doesn't matter if you do (1 + 2) first or (3 * 4). Precedence would only be relevant if you applied both operators to the same `x`

      What's relevant here is the _order of evaluation_. The order of evaluation says that `++x` and `x++` have to be evaluated before the `-` can be evaluated. But nothing says if `++x` or `x++` has to be evaluated first.

  • alsaht

    int add(int x, int y) {
        return x - y ;
    }

    int main()
    {
        int x{ 5 };

        int y = add(++x, x++);
        std::cout << y << '\n';

    }
    when i tried to run this program (on visual studio 2019)
    the result was : 1
    that was very confused

    so i tried to return x and y from function add  the results was :
    x = 6
    y = 5

    how ?

    if the compiler evaluate from left to right x and y  in function add should be :
    x = 6
    y = 6

    if the compiler evaluate from right to left the results should be :
    x = 7
    y = 5

    i tried  to do this with a variable instead a function :

    int y = ++x - x++ ;
    cout << y;
    the result is : 0

    the compiler evaluate from left to right here

    sorry for my bad english :)

  • thanksForTheGreatTutorials

    int x = 6;
    expression 6 has a side effect it changes the state of x (the garbage value)
    so every statement should have at least a one side effect !!

    if a statement doesn't have a side effect it's actually useless

    An example :
    3+2*6;

    am i right ?

  • keshav

    This prints:

    6 5

    how does this code remember the value of x and then shows the value of x as 5 ?
    programming languages don't remember these things.

    • echo cassette

      When we execute the first statement, the 5th line, we instantiate the variable x to be 5. Next we will evaluate the right-hand side of the assignment.

      This will look at the value stored in x and copy this in to a temporary variable (lets call it temp). So in memory we will have two variables: x := 5 and temp := 5.

      We will then add one to the value stored in x and update x to this value, so in memory we will have x := 6 and temp := 5.

      Next the value stored in temp (the original value of x: 5) will be assigned to y and this temporary variable is discarded. Our picture of memory is now x := 6 and y := 5.

      Finally, line 8 will print these out as we can see in the output: "6 5".

      To summarise, a temporary variable is used to 'remember' the value of x.

      (It is likely that the compiler would in fact optimise this temporary variable away and simply update y in the first instance. But in general, thinking of this as using a temporary variable won't steer you wrong).

    • 3li Mot 3alim

      may be it is better if you check a previous less.(3.8 Using an integrated debugger: Watching variables)

  • Greg

    I saw something a little surprising with gcc on the "Side Effects" program. I tried both:

    value = add(x, ++x)
       and
    value = add(++x, x)

    Note that I explicitly set the value of x back to 5 prior to the reversed arguments.

    In both cases, the result was 12.  This would make sense to me if the parameters of the "add" method were references, but with pass-by-value this is unexpected.  Interestingly, gcc did issue warnings about the operation:

    ../src/SideEffects.cpp:16:27: warning: operation on 'x' may be undefined [-Wsequence-point]
         int value = add(x, ++x);
                               ^
    ../src/SideEffects.cpp:20:24: warning: operation on 'x' may be undefined [-Wsequence-point]
         value = add(++x, x);
                            ^

    Given the warning, it looks like the authors of gcc were clever enough to evaluate all of the arguments before copying their values.

    Even more clever:  I changed the parameters of "add" to be references, and gcc no longer issued the warnings about undefined operations.  That seems suspicious to me, though.  I thought the caller wasn't supposed to know implementation details like that, but the suppression of the warnings is indicative of implementation details of the  called method.

  • sami

    I have two questions:

    1) the following code's output is too weird.
    it is :
    9 8 9

    2 )
    "There are other cases where C++ does not specify the order in which certain things are evaluated (such as operator operands)"

    Would you please give an example above that? As far as I remember, there is a chart about associativity and operators precedence. But why still you stated that C++ does not specify the order in which certain things are evaluated (such as operator operands)?

    • nascardriver

      1) Your code causes undefined behavior, because you're modifying and accessing `x` in an undefined order.
      2) No promises on this on. As far as I know, the following may produce 1,2 or 2,1

      Operator precedence and associativity doesn't affect this example. If you had

      It would be evaluated as

      But the order in which `fn` is called is still undefined.

      • 3li Mot 3alim

        Hi @nascardriver
        do you think you can give more information about point one?

        • nascardriver

          The order of evaluation of arguments is not specified. Maybe `x` is evaluated first, maybe `x += 3` is evaluated first, we don't' know.
          It is also not specified when the side effects of the modifications to `x` will take place. They will happen before the function is called, but before that there are no rules. Maybe `x++` is evaluated, then `x += 3` is evaluated, then `x` gets incremented from the `x++`.
          If there are multiple side effects to a variable and no order is specified, behavior is undefined.

  • sami

    "int y = x++; // x is incremented to 6, copy of original x is evaluated to the value 5, and 5 is assigned to y
    "
    Shouldn't the order of the explanations changed? Because in the detailed explanations, you said, first copy of original x is evaluated to the value 5. I think the statements above is kind of confusing with that order.

  • Raffaello

    I'm having a bit of a hard time fully grasping what pre/and post-increment do, even when following the rule of not using more than one variable with a side effect in a single statement, I may end up avoiding them all together :D but at least I have a somewhat basic understanding of them when I have to read it in other people's code. I have a few questions:
    1. is preferring pre-increment to post is something generally said by the community, established as a best practice or your personal opinion?
    2. I see a lot of programmers use the post-increment over the pre, why is that? is the post-fix preferred in some cases?
    3. since it's difficult to understand which side effect is applied first in a given statement, is there a way to look the state of a variable operator per operator in a single line on a debugger, similar to the command "next line"?
    4. why would you need two operators that increment the value of a given variable? isn't one enough? I guess this has more to do with how they wanted to design the language itself, but still, I'm curious :)

    • nascardriver

      1. pre- and post-increment do different things. Post-increment can be dramatically more expensive than pre-increment. Using post-increment when you don't need it is plain wrong, not because of a convention or an opinion, but because it does more than required. It's like ordering a pizza because you need a slice of tomato.

      2. They might be used to post-increment from other languages where there is no pre-increment or there is no difference. Or they didn't learn or care about the difference. In some cases, you need post-increment, though it's not common.

      3. Some debuggers support in-line breakpoints. You'll have to look up how to do that with your debugger. Keep in mind that your observations do not imply that the execution order is the same when you compile your code with another compiler. Depending on your code, the order of execution may be undefined.

      4. They do different things. Here's an example of how they work

      Output
      A: 4
      B: 5
      C: 5
      D: 5
      E: 6

      Now think about what happens if `i` wasn't an `int`, but a really large type. The post-increment operator has to create a copy of it, and copying data is expensive (Both for memory and speed).
      Unless you need the old value of a variable, there's no reason to use the post-increment operator.
      The same applies to the decrement operators.

  • Ambareesh

    Postfix increment (post-increment)    x++    Copy x, then increment x, then return the copy

    Wouldn't this be better explained as:
    x++ Return x, and increment it afterwards

    and
    ++x Increment x, and then return it

    I find this is easier to understand and remember (you just need to follow where ++ occurs). The part about "returning the copy" seems unnecessary since the copy is discarded anyway.

    Thoughts?

    • nascardriver

      A `return` stops execution. If you say "Return x, and increment it afterwards", the "and increment it afterwards" part is never executed. Although this explanation works in a non-programming context, it's confusing here.

  • Lifthra

    But why can't the compiler understand wich way to elevate (x, ++x)?

    • nascardriver

      Some things don't get standardized, because it would potentially decrease performance of programs. By leaving the order of execution up to the compiler, the compiler can choose the order that runs the fasted on the target machine.

  • HolzstockG

    So as I understand "side effect" is nothing but change in states of already defined variables and functions?

  • Bowie

    if prefix is recommended, then why there are so many people still using postfix in for loop.
    usually i see something like this:

    for (int i=0; i<5; i++) {
        std::cout << i;
    }

  • jfzcoman

    Hi,Alex,I've got confused after saw following form http://a4academics.com/interview-questions/57-c-plus-plus/725-c-interview-questions-for-freshers?showall=&start=2
    23) What will be output of the following code snippet?

    int num1=5;
    int num2=5;
    num1 =num1++ + num1--;
    num2 =++num2 + --num2;
    printf("num1=%d num2=%d",num1,num2);

    Output will be num1=10 num2=10.

    is this right? I thought it wouble num1=11

  • 5irKawfycups

    I feel like there may be a typo, or maybe I am misunderstanding? In the section "Side effects", the explanation under the first code block

    Here, part of the explanation reads: "Even after the statement has finished executing, x will still have the value 5."

    Wouldn't a reference to x now return 6, not 5? For that matter, wouldn't x++ also cause a later reference to x to return 6?

    • nascardriver

      Hi!

      The sentence you quoted refers to `x = 5`, not the other 2 statements.

      That's what the sentence is trying to say.

  • Avijit Pandey

    I have a very weird question that was handed to me by my professor, it's actually in C but I'm posting the C++ equivalent here since the outputs and the problems I'm encountering remain the same.

    I'm assuming here that when we do

    it evaluates as 2 + 3 + x. For x, if we assigned a new variable-the value of 'a++', it would be assigned 3 and 'a' would be incremented. But since we are directly adding a++, it's adding the value at the memory address of 'a' after evaluation of the expression 'a++'which will be 4. So the output should be 9. And using similar logic, the final Output should be 9, 6, 10.
    However, the actual output is 15, 4 and 6.

    Now, obviously, my evaluation order is not being followed by the compiler. But I can not figure out how on earth the first expression evaluates to 15!

    2.One other weird thing that I noticed is that when I define 3 pointer variables to hold the memory addresses of 'a', 'b', and 'd' respectively(and never modify them), the output magically changes to 14, 4, and 5.

    I do have a feeling I'm making it way too complicated than it needs to be, but I am seriously annoyed by this one line of code. Can you guys explain this please?

    • The output is unspecified in C++ and C, assuming that the original code is

      For one, the order of evaluation of the arguments is undefined, ie. it could be
      (1) -> (2) -> (3)
      (2) -> (1) -> (3)
      (3) -> (1) -> (2)
      ...

      I'm fairly sure that each part (1), (2), and (3) produces undefined behavior too before C++11 and in C. I'm not sure about C++11 onward, because sequencing rules changed.

      The question cannot be answered.

      > But I can not figure out how on earth the first expression evaluates to 15!
      Undefined behavior. You code might as well change your wallpaper.

      2.
      Undefined behavior.

      Your professor can't expect an answer other than that the code produces undefined behavior. This code never produced well defined output.

  • Nikola

    Hello,

    Not technically important but there is a mistake in the first sentence of the "Side effects" section.

    A function or expression is said to have a side effect if IT does

  • Attila

    I feel like the third paragraph from the bottom should be outlined as a "Rule" shown in lecture 0.2

  • Murat

    When running both commented lines (individually), the output is 2. But you said that it would be 1 or 2 (depending on which is commented out).

    • Pre C++17, both lines cause undefined behavior. There's no point in talking about what the result could be, because anything could happen.

      As I understand it, since C++17 this is guaranteed

      Either I am wrong or gcc and clang are generating invalid warnings. Maybe someone else can clear up.
      My assumption is based on rule 20 here https://en.cppreference.com/w/cpp/language/eval_order#Rules

  • DecSco

    Does this produce undefined behaviour?

    On my machine, it works fine. But I'm not sure that's universal, given that the execution may already have returned to the function call.

    • This is well-defined.

      > the execution may already have returned to the function call
      Mind elaborating? Your program is executed sequentially, it can't run 2 things at the same time.

      • DecSco

        Cheers. That's what I needed to know.
        What I meant is that at the time when the return statement gets executed, and the value of "identifier" is supposed to be returned to the caller, it still has to be incremented. So if the execution were to return to the caller at the point at which the value is correct, then the increment would be lost, because the execution has already left the function.
        So the compiler would actually have to implement it as something like

        Correct?

        • Side effect are applied when the function ends, `main` won't run before `identifier` has been incremented. You'll only lose information if you modify a variable multiple times in 1 statement, but you're not doing that.

  • I found these examples really amusing and informative. Thanks for the lesson.

  • daileyj93

    "For more information on undefined behaviors, revisit the “Undefined Behavior” section of lesson 1.3 -- Introduction to variables."

    It looks like undefined behavior is now lesson 1.6 -- Uninitialized variables and undefined behavior, not lesson 1.3 anymore.

  • zakqux

    Can you clarify this? Don't understand quite how x=1...

    • postfix++ returns the value the variable had before it has been increased.
      ++prefix returns the value the variable has after having been increased.

      Both variants increase the variable by one, only the return value is different. Use ++prefix unless you need postfix++. postfix++ is slower.

  • Jörg

    Hi, I heard that c++17 introduced some changes to the evaluation order when working with side effects. Do you know any of this? Is

    still undefined?

    • Alex

      It did introduce some evaluation order changes, and some of the expressions that yielded undefined results now resolve determinately. I don't know if those particular expressions are affected or not. The best practice doesn't change though -- don't use a variable with a side effect applied to it more than once in an expression.

  • Senna

    Hi,
    I've read previously lessons, even though they're updated do I gotta study them again ?

Leave a Comment

Put all code inside code tags: [code]your code here[/code]