18

I am writing a small test app in C with GCC 4.8.4 pre-installed on my Ubuntu 14.04. And I got confused for the fact that the expression a=(b++); behaves in the same way as a=b++; does. The following simple code is used:

#include <stdint.h>
#include <stdio.h>

int main(int argc, char* argv[]){
    uint8_t a1, a2, b1=10, b2=10;
    a1=(b1++);
    a2=b2++;

    printf("a1=%u, a2=%u, b1=%u, b2=%u.\n", a1, a2, b1, b2);

}

The result after gcc compilation is a1=a2=10, while b1=b2=11. However, I expected the parentheses to have b1 incremented before its value is assigned to a1.

Namely, a1 should be 11 while a2 equals 10.

Does anyone get an idea about this issue?

user2864740
  • 60,010
  • 15
  • 145
  • 220
user5055706
  • 271
  • 2
  • 5
  • 17
    post increment operator works always after an end of statement. Parenthesis doesn't do much to it. – askmish Jun 27 '15 at 10:14
  • 4
    @askmish; It is not necessary that post increment operator work after the end of the statement. It is also possible that it may work at the beginning. – haccks Jun 27 '15 at 11:07
  • @haccks I agree with your statement. My comment was specific to this question only. – askmish Jun 27 '15 at 13:19
  • 7
    @askmish: "post increment operator works always after an end of statement" - this is absolutely incorrect. What does "works" mean anyway? Operators have results and side-effects, which are two independent matters. Which one does "works" refer to? In any case this comment is incorrect even in the context of this specific example. – AnT stands with Russia Jun 27 '15 at 16:57
  • 3
    `b1` *is* incremented before its value is assigned to `a1` (or at least: it behaves as if it was). The result of `b1++` is the value of `b1` *before* the increment, and parentheses don't change that. – user253751 Jun 28 '15 at 04:50
  • 2
    Maybe `((b++))` or `(((b++)))` will do the trick. – xehpuk Jun 28 '15 at 15:31
  • 1
    or just use ++b cuz its made for that purpose – rahul tyagi Jun 28 '15 at 18:28

10 Answers10

37

However, I expected the parentheses to have b1 incremented before its value is assigned to a1

You should not have expected that: placing parentheses around an increment expression does not alter the application of its side effects.

Side effects (in this case, it means writing 11 into b1) get applied some time after retrieving the current value of b1. This could happen before or after the full assignment expression is evaluated completely. That is why a post-increment will remain a post-increment, with or without parentheses around it. If you wanted a pre-increment, place ++ before the variable:

a1 = ++b1;
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
35

Quoting from the C99:6.5.2.4:

The result of the postfix ++ operator is the value of the operand. After the result is obtained, the value of the operand is incremented. (That is, the value 1 of the appropriate type is added to it.) See the discussions of additive operators and compound assignment for information on constraints, types, and conversions and the effects of operations on pointers. The side effect of updating the stored value of the operand shall occur between the previous and the next sequence point.

You can look up the C99: annex C to understand what the valid sequence points are.

In your question, just adding a parentheses doesn't change the sequence points, only the ; character does that.

Or in other words, you can view it like there's a temporary copy of b and the side-effect is original b incremented. But, until a sequence point is reached, all evaluation is done on the temporary copy of b. The temporary copy of b is then discarded, the side effect i.e. increment operation is committed to the storage,when a sequence point is reached.

askmish
  • 6,464
  • 23
  • 42
24

Parentheses can be tricky to think about. But they do not mean, "make sure that everything inside happens first".

Suppose we have

a = b + c * d;

The higher precedence of multiplication over addition tells us that the compiler will arrange to multiply c by d, and then add the result to b. If we want the other interpretation, we can use parentheses:

a = (b + c) * d;

But suppose that we have some function calls thrown into the mix. That is, suppose we write

 a = x() + y() * z();

Now, while it's clear that the return value of y() will be multiplied by the return value of z(), can we say anything about the order that x(), y(), and z() will be called in? The answer is, no, we absolutely cannot! If you're at all unsure, I invite you to try it, using x, y, and z functions like this:

int x() { printf("this is x()\n"); return 2; }
int y() { printf("this is y()\n"); return 3; }
int z() { printf("this is z()\n"); return 4; }

The first time I tried this, using the compiler in front of me, I discovered that function x() was called first, even though its result is needed last. When I changed the calling code to

 a = (x() + y()) * z();

the order of the calls to x, y, and z stayed exactly the same, the compiler just arranged to combine their results differently.

Finally, it's important to realize that expressions like i++ do two things: they take i's value and add 1 to it, and then they store the new value back into i. But the store back into i doesn't necessarily happen right away, it can happen later. And the question of "when exactly does the store back into i happen?" is sort of like the question of "when does function x get called?". You can't really tell, it's up to the compiler, it usually doesn't matter, it will differ from compiler to compiler, if you really care, you're going to have to do something else to force the order.

And in any case, remember that the definition of i++ is that it gives the old value of i out to the surrounding expression. That's a pretty absolute rule, and it can not be changed just by adding some parentheses! That's not what parentheses do.

Let's go back to the previous example involving functions x, y, and z. I noticed that function x was called first. Suppose I didn't want that, suppose I wanted functions y and z to be called first. Could I achieve that by writing

x = z() + ((y() * z())?

I could write that, but it doesn't change anything. Remember, the parentheses don't mean "do everything inside first". They do cause the multiplication to happen before the addition, but the compiler was already going to do it that way anyway, based on the higher precedence of multiplication over addition.

Up above I said, "if you really care, you're going to have to do something else to force the order". What you generally have to do is use some temporary variables and some extra statements. (The technical term is "insert some sequence points.") For example, to cause y and z to get called first, I could write

c = y();
d = z();
b = x();
a = b + c * d;

In your case, if you wanted to make sure that the new value of b got assigned to a, you could write

c = b++;
a = b;

But of course that's silly -- if all you want to do is increment b and have its new value assigned to a, that's what prefix ++ is for:

a = ++b;
Steve Summit
  • 45,437
  • 7
  • 70
  • 103
6

Your expectations are completely unfounded.

Parentheses have no direct effect on the order of execution. They don't introduce sequence points into the expression and thus they don't force any side-effects to materialize earlier than they would've materialized without parentheses.

Moreover, by definition, post-increment expression b++ evaluates to the original value of b. This requirement will remain in place regardless of how many pair of parentheses you add around b++. Even if parentheses somehow "forced" an instant increment, the language would still require (((b++))) to evaluate to the old value of b, meaning that a would still be guaranteed to receive the non-incremented value of b.

Parentheses only affects the syntactic grouping between operators and their operands. For example, in your original expression a = b++ one might immediately ask whether the ++ apples to b alone or to the result of a = b. In your case, by adding the parentheses you simply explicitly forced the ++ operator to apply to (to group with) b operand. However, according to the language syntax (and the operator precedence and associativity derived from it), ++ already applies to b, i.e. unary ++ has higher precedence than binary =. Your parentheses did not change anything, it only reiterated the grouping that was already there implicitly. Hence no change in the behavior.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
5

Parentheses are entirely syntactic. They just group expressions and they are useful if you want to override the precedence or associativity of operators. For example, if you use parentheses here:

a = 2*(b+1);

you mean that the result of b+1 should be doubled, whereas if you omit the parentheses:

a = 2*b+1;

you mean that just b should be doubled and then the result should be incremented. The two syntax trees for these assignments are:

   =                      =
  / \                    / \
 a   *                  a   +
    / \                    / \
   2   +                  *   1
      / \                / \
     b   1              2   b

a = 2*(b+1);            a = 2*b+1;

By using parentheses, you can therefore change the syntax tree that corresponds to your program and (of course) different syntax may correspond to different semantics.

On the other hand, in your program:

a1 = (b1++);
a2 = b2++;

parentheses are redundant because the assignment operator has lower precedence than the postfix increment (++). The two assignments are equivalent; in both cases, the corresponding syntax tree is the following:

    =
   / \
  a   ++ (postfix)
      |
      b

Now that we're done with the syntax, let's go to semantics. This statement means: evaluate b++ and assign the result to a. Evaluating b++ returns the current value of b (which is 10 in your program) and, as a side effect, increments b (which now becomes 11). The returned value (that is, 10) is assigned to a. This is what you observe, and this is the correct behaviour.

nickie
  • 5,608
  • 2
  • 23
  • 37
  • 1
    While this is all correct, I'm not sure it addresses the confusion. I can imagine how one can read `a2 = b2++;` and float the `++` off to the side in some sense to mean `a2 = b2, then increment b2`, rather than `a2 = {return value of b2++}`, and how parentheses might change how far one thinks the `++` gets floated. –  Jun 27 '15 at 19:35
  • @Hurkyl, you mean if one forgets that "the assignment operator has lower precedence than the postfix increment"? – nickie Jun 27 '15 at 21:12
  • 1
    @Hurkyl The explanation, albeit near-perfect and showing lots of mathematical-logical background, was just written for the wrong target audience IMHO. Whilst this is a great means to explain that matter to computer science students, I think I have enough experience in this field to judge from the OP's wording that the OP will require a much more simplistic explanation, as I don't assume he has ever heard of "syntax trees". – syntaxerror Jul 03 '15 at 23:00
3

OK, in a nutshell: b++ is a unary expression, and parentheses around it won't ever take influence on precedence of arithmetic operations, because the ++ increment operator has one of the highest (if not the highest) precedence in C. Whilst in a * (b + c), the (b + c) is a binary expression (not to be confused with binary numbering system!) because of a variable b and its addend c. So it can easily be remembered like this: parentheses put around binary, ternary, quaternary...+INF expressions will almost always have influence on precedence(*); parentheses around unary ones NEVER will - because these are "strong enough" to "withstand" grouping by parentheses.

(*)As usual, there are some exceptions to the rule, if only a handful: e. g. -> (to access members of pointers on structures) has a very strong binding despite being a binary operator. However, C beginners are very likely to take quite awhile until they can write a -> in their code, as they will need an advanced understanding of both pointers and structures.

syntaxerror
  • 661
  • 2
  • 6
  • 24
  • What you said, if taken literally, is obviously false. I think what you're trying to say is that, in C, all unary operators have higher precedence than binary operators. This is almost true. But there are some binary operators that have very high precedence, such as -> and []. Check out a precedence chart, such as this one: http://en.cppreference.com/w/c/language/operator_precedence – Mark VY Jun 27 '15 at 20:19
  • 1
    Well, I just note that in the precedence chart you've linked to, parentheses meant to *group expressions* (and thus to gain precedence as in my `a * (b + c)` example) are not listed at all! But even in a decades-old C book that I had worked myself through in my youth, it DOES also mention these parentheses, and AFAIR as the very first. Anyways, I've improved my wording a little. – syntaxerror Jun 27 '15 at 22:24
3

However, I expected the parentheses to have b1 incremented before its value is assigned to a1.

You aren't assigning b1 to a1: you're assigning the result of the postincrement expression.

Consider the following program, which prints the value of b when executing assignment:

#include <iostream>
using namespace std;

int b;

struct verbose
{
    int x;

    void operator=(int y) {
        cout << "b is " << b << " when operator= is executed" << endl;  
        x = y;
    }
};

int main() {
    // your code goes here
    verbose a;
    b = 10;
    a = b++;
    cout << "a is " << a.x << endl;
    return 0;
}

I suspect this is undefined behavior, but nonetheless when using ideone.com I get the output shown below

b is 11 when operator= is executed
a is 10
2

The parentheses will not change the post-increment behaviour itself.

a1=(b1++); //b1=10

It equals to,

 uint8_t mid_value = b1++; //10
 a1 = (mid_value); //10
Eric Tsui
  • 1,924
  • 12
  • 21
1

Placing ++ at the end of a statement (known as post-increment), means that the increment is to be done after the statement.

Even enclosing the variable in parenthesis doesn't change the fact that it will be incremented after the statement is done.

From learn.geekinterview.com:

In the postfix form, the increment or decrement takes place after the value is used in expression evaluation.

In prefix increment or decrement operation the increment or decrement takes place before the value is used in expression evaluation.

That's why a = (b++) and a = b++ are the same in terms of behavior.

In your case, if you want to increment b first, you should use pre-increment, ++b instead of b++ or (b++).

Change

a1 = (b1++);

to

a1 = ++b1; // b will be incremented before it is assigned to a.
Community
  • 1
  • 1
raymelfrancisco
  • 828
  • 2
  • 11
  • 21
  • 2
    The reference you've used reads as though it was written by someone who doesn't know English very well, and though that in itself isn't a bad thing I think anybody who's attempting to teach C in English should certainly know both C and English very well. As a result, I must question the authors ability to understand C. Indeed, it took me less than five minutes to find an error; I went straight to [this page](http://www.c4learn.com/c-programming/c-integer-data-type/). [This page](http://www.c4learn.com/c-programming/c-decide-integer-type/) is no better, either. – autistic Jun 27 '15 at 12:07
  • @undefinedbehavior: How about [this one](http://www.eskimo.com/~scs/cclass/notes/sx7b.html)? Any better? :-) – Steve Summit Jun 27 '15 at 12:44
  • 2
    Not to mention that the very first code example (b=y++;) is followed by this 'explanation': In this example suppose the value of variable ‘x’ is 5 then value of variable ‘b’ will be 5 because old value of ‘x’ is used. x?? – Eight-Bit Guru Jun 27 '15 at 19:12
  • There are plenty of good quality references out there. No need to cite or quote junk like this. – user207421 Jun 28 '15 at 00:56
  • @raymelfrancisco I'd prefer the reference that author mentions in the first line: "[This section corresponds to K&R Sec. 2.8]"... but this makes me feel better about upvoting. – autistic Jun 29 '15 at 01:31
1

To make it short: b++ is incremented after the statement is done

But even after that, the result of b++ is put to a.

Because of that parentheses do not change the value here.

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
Coder55
  • 559
  • 5
  • 17