9

test.(c/cpp)

#include <stdio.h>

int main(int argc, char** argv)
{
  int a = 0, b = 0;
  printf("a = %d, b = %d\n", a, b);
  b = (++a)--;
  printf("a = %d, b = %d\n", a, b);

  return 0;
}

If I save the above as a .cpp file, it compiles and outputs this upon execution:

a = 0, b = 0
a = 0, b = 1

However, if I save it as a .c file, I get the following error:

test.c:7:12: error: lvalue required as decrement operator.

Shouldn't the (++a) operation be resolved before the (newValue)-- operation? Does anyone have any insight on this?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
mkosler
  • 322
  • 2
  • 7
  • 4
    `b = (++a)--;` <- isn't it **undefined behaviour**? – LihO Feb 10 '13 at 14:53
  • 3
    @LihO: Why? The increment on `a` is sequenced before its evaluation – Andy Prowl Feb 10 '13 at 14:54
  • Why not just `b = a + 1` – Gaurav Agarwal Feb 10 '13 at 14:54
  • @AndyProwl: I always avoid using more preincrement/postincrement operations within the same line... I don't want to end up with something like this: `b = ++a + ++a;`, which is already UB as far as I know. – LihO Feb 10 '13 at 14:57
  • @LihO: I agree, and you are right in the last example you provide, but the one in the OP's question is not UB IMO – Andy Prowl Feb 10 '13 at 14:57
  • 1
    I think the real lesson to understand is that fewer lines of C code do not make a faster program. It would be better to write 2 or 3 lines instead. Its more obvious and easier to understand. I'd recommend only using predecrement/increment and only use post decrement/increment for very special situations (i.e. you can't figure out any other way to write it). – Josh Petitt Feb 10 '13 at 15:32
  • For those asking "Why?", I have a std::list where I want to do an operation on all pairs within the std::list. I wanted a nice way to, within the for loop, assign the second iterator to the first + 1. For example: `std::list::iterator it, jt; for (it = _list.begin(); it != _list.end(); it++) for (jt = (++it)--; jt != _list.end(); jt++) // operate on it and jt` – mkosler Feb 10 '13 at 15:41
  • @LihO, yes. You are changing `a` twice (`++` and `--`) between sequence points (start of the lhs and assignment to `b`). And this is illegal in C and C++. As undefined behaviour, _anything_ can happen. Even what you expect. All up to the compiler's mood, and the moon's phase. – vonbrand Feb 11 '13 at 21:08

4 Answers4

16

In C the result of the prefix and postfix increment/decrement operators is not an lvalue.

In C++ the result of the postfix increment/decrement operator is also not an lvalue but the result of the prefix increment/decrement operator is an lvalue.

Now doing something like (++a)-- in C++ is undefined behavior because you are modifying an object value twice between two sequence points.

EDIT: following up on @bames53 comment. It is undefined behavior in C++98/C++03 but the changes in C++11 on the idea of sequence points now makes this expression defined.

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
ouah
  • 142,963
  • 15
  • 272
  • 331
  • 3
    C++11 does away with sequence points and instead only requires that the reads and modifies be ordered, which they are in `(++a)--`. See [here](http://stackoverflow.com/q/10655290/365496) – bames53 Feb 10 '13 at 15:03
  • DIsagree with the claim that `(++a)--` is well-defined. The page that bames53 links to would apply to `--(++a)` but post-increment does not have the same sequencing – M.M Jan 16 '18 at 20:48
  • @M.M The side effects of `++a` in `(++a)--` are sequenced relative to the postfix `--` as per C++11, 5.17p1: *"In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression."* (with 5.3p2 *"If x is not of type bool, the expression ++x is equivalent to x+=1"*). – ouah Jan 16 '18 at 22:05
  • @ouah the assignment involved in `++a` is not sequenced w.r.t the decrement (i'm not sure what part of the quote you think is relevant -- the decrement is not one of the operands of `a+=1`). Maybe a new question would be appropriate though instead of argument in comments – M.M Jan 16 '18 at 22:58
  • @M.M I'm referring to the second part of the quote *"the assignment is sequenced [...] before the value computation of the assignment expression"*. And as we know that for postfix `--` (5.2.6p1) *"the value computation of the -- expression is sequenced before the modification of the operand object."*, for the me the side effects of `++a` should happen before those of `--`. I'm no longer much active in SO, so if you are not convinced can you please ask a new question and of course, if I'm wrong I'd be glad to amend my answer. – ouah Jan 17 '18 at 11:01
3

In C and C++, there are lvalue expressions which may be used on the left-hand side of the = operator and rvalue expressions which may not. C++ allows more things to be lvalues because it supports reference semantics.

++ a = 3; /* makes sense in C++ but not in C. */

The increment and decrement operators are similar to assignment, since they modify their argument.

In C++03, (++a)-- would cause undefined behavior because two operations which are not sequenced with respect to each other are modifying the same variable. (Even though one is "pre" and one is "post", they are unsequenced because there is no ,, &&, ?, or such.)

In C++11, the expression now does what you would expect. But C11 does not change any such rules, it's a syntax error.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
2

For anybody who might want the precise details of the differences as they're stated in the standards, C99, §6.5.3/2 says:

The value of the operand of the prefix ++ operator is incremented. The result is the new value of the operand after incrementation.

By contrast, C++11, §5.3.2/1 says:

The result is the updated operand; it is an lvalue, and it is a bit-field if the operand is a bit-field.

[emphasis added, in both cases]

Also note that although (++a)-- gives undefined behavior (at least in C++03) when a is an int, if a is some user-defined type, so you're using your own overloads of ++ and --, the behavior will be defined -- in such a case, you're getting the equivalent of:

a.operator++().operator--(0);

Since each operator results in a function call (which can't overlap) you actually do have sequence points to force defined behavior (note that I'm not recommending its use, only noting that the behavior is actually defined in this case).

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
0

§5.2.7 Increment and decrement:

The value of a postfix ++ expression is the value of its operand. [ ... ]  The operand shall be a modifiable lvalue.

The error you get in your C compilation helps to suggest that this is only a feature present in C++.

David G
  • 94,763
  • 41
  • 167
  • 253