56

I have been asked the following question in a test (I didn't want to write it myself. The test asked it. I know its bad code still) about evaluating ++*ptr++

int Ar[ ] = { 6 , 3 , 8 , 10 , 4 , 6 , 7} ;
int *Ptr = Ar  ;
cout<<++*Ptr++  ;

However, I suspect this is undefined behavior since it can be both (++*ptr)++ or ++(*ptr++). Is it? I am not too well acquainted with documentation so I couldn't find anything.

Prakash Pazhanisamy
  • 997
  • 1
  • 15
  • 25
Harmonic
  • 723
  • 5
  • 11
  • 77
    What did C++ do to you that you feel compelled to do this? – tadman Dec 20 '18 at 18:40
  • 1
    No it is well-formed. – Igor R. Dec 20 '18 at 18:41
  • 3
    When asking about "undefined behaviour" the best approach is to look in the documentation first. – tadman Dec 20 '18 at 18:41
  • Independently from its well-formedness, why would you ever write such code? – Matteo Italia Dec 20 '18 at 18:41
  • 4
    @soham: Generally speaking, asking about complex interactions of multiple `++` instances and such, which represents code that should *never be written*, will attract downvotes. Or to put it another way, if you have to ask if it's well-defined, don't write it that way. – Nicol Bolas Dec 20 '18 at 18:41
  • 1
    @tadman Sorry but I didn't understand your point – Harmonic Dec 20 '18 at 18:42
  • 7
    @soham Why do you think it's undefined? I think you'd get less downvotes if you explained that in the question. – HolyBlackCat Dec 20 '18 at 18:42
  • 1
    Combining pre and postfix increment operators is cruel to anyone trying to read this code and make sense of your intent. If you suspect this is undefined behaviour the first step is to look at the C++ specification or a good reference book on the subject of pre and post increment operators and how they can work with a de-referencing pointer operation. – tadman Dec 20 '18 at 18:42
  • 4
    @MatteoItalia This was asked in a test, sir ... I didn't want to write it myself – Harmonic Dec 20 '18 at 18:43
  • 1
    If you really want to do this, probably try rewriting it as `++(*(ptr++))`. Easier to understand. – Pavan Yalamanchili Dec 20 '18 at 18:43
  • 1
    @soham Generally, ugly expressions like this one cause UB if/when they change a variable more than once in one place. Since this one doesn't (one `++` changes the pointer, another `++` changes a pointed `int`), it's well-defined. – HolyBlackCat Dec 20 '18 at 18:45
  • @tadman actually if you suspect it is undefined the first and only step is to rewrite expression eliminating the doubt. – Slava Dec 20 '18 at 18:47
  • 1
    @soham correct answer to that test "person, who wrote this should be fired" – Slava Dec 20 '18 at 18:48
  • You should have tagged the question as language lawyer. Than comments like 'why did you do it' would not be applicable, and the question would have been taken on it's merit. – SergeyA Dec 20 '18 at 18:50
  • Fired might be excessive, but they certainly get the stink-eye during the code review and head off to rewrite before a second code review. If they flunk that review for the same reasons... – user4581301 Dec 20 '18 at 18:50
  • 2
    That's easy; it's the same as `cout<<(*Ptr+++=1);` ;-) – molbdnilo Dec 20 '18 at 19:22
  • I think this is actually a good _test question_, because what it's testing is whether you understand that `++*ptr` does something different than `*++ptr`, and formulating it in this way forces the test-taker to engage their brain rather than regurgitating a memorized fact. – zwol Dec 20 '18 at 20:24
  • 1
    This code has a well-defined behaviour of the developer being slapped hard on their head by whoever needs to support this. – IMil Dec 21 '18 at 00:18
  • 1
    @zwol: OTOH, it's a bad question for anyone who (like me) uses parens to force evaluation order, rather than depending on my memory of precedence rules. I've got only so many brain cells, you know, and have better things to do with them :-) – jamesqf Dec 21 '18 at 03:01
  • could also be written as `++Ptr++[0]` – M.M Dec 21 '18 at 07:19
  • And this is, why every C/C++ programmer should have their operator precedence table under their keyboard (https://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Operator_precedence)... – cmaster - reinstate monica Dec 21 '18 at 11:41
  • I'd prefer `++(++Ptr)[-1]` or even `++-1[++Ptr]`, because pre-increment is always better than post-increment (this comment may contain sarcasm). – chtz Dec 21 '18 at 12:36

2 Answers2

76

However I suspect this is undefined behaviour since it can be both (++*ptr)++ or ++(*ptr++). Is it?

Not really, unlike the runtime behavior, which gives ample leeway to implementors, in C++ parsing itself follows quite strict and well-defined rules1. Indeed, looking at the precedence rules, ++*Ptr++ is actually parsed as ++(*(Ptr++)).

This trick question instead is probably alluding to the undefined behavior of expressions such as i = ++i + ++i, where you have a value that appears multiple times in an expression, and is subjected to a modification by a side-effect of the expression itself. Such expressions are illegal, as, unless there's some operator that sequences the side effects2, the exact moment in which they are applied is not defined, so it's undefined exactly what values i would assume in the various points of the expression.

Still, there's no undefined behavior here, as all side effects in the expression operate on different values, which appear only once in the expression: the "inner" ++ affects Ptr, while the outer one affects the value pointed originally by Ptr, i.e. Ar[0].

++(*(Ptr++))
     ^^^^^____increments Ptr, returning its original value
   ^^^^^^^^______dereferences the original Ptr, AKA &Ar[0]
^^^^^^^^^^^^_______ increments Ar[0]

That being said, if I ever saw such an expression in a code base of ours I'd go to great lengths to find the author and make sure that this wouldn't happen again.


  1. If sometimes very bizarre and absurdly costly to implement. Still, there are instances of undefined behavior in the standard describing some corner cases of the parsing, but it's orders of magnitude less pervasive than "runtime" undefined behavior.
  2. A handy summary of those rules can be found here; interestingly, some extra guarantees have been added in C++17.
Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • @val what made it legal? – Krupip Dec 20 '18 at 20:28
  • 1
    The behavior of `i = ++i + ++i` is undefined in C++17 – Jans Dec 20 '18 at 20:32
  • 3
    @val: just checked again: it's not legal. C++17 just added a sequence point for assignment and compound assignment operators. https://stackoverflow.com/a/46171943/214671 `++i + ++i` remains illegal even just by itself (without assignment on the left). – Matteo Italia Dec 20 '18 at 20:33
  • @MatteoItalia more formally [intro.execution](http://eel.is/c++draft/intro.execution#10) the operands of addition operator are unsequenced – Jans Dec 20 '18 at 20:38
  • Also, [on cppreference](https://en.cppreference.com/w/cpp/language/eval_order#Rules); from point 14 are the modifications introduced in C++17; they are quite something (and significantly more than what the answer I linked above), but they don't include addition. – Matteo Italia Dec 20 '18 at 20:40
  • What if `*Ptr` references `Ptr` itself, would that make it undefined behaviour? – Bergi Dec 20 '18 at 22:53
  • Re: "Such expressions [as `i = ++i + ++i`] are illegal, as, unless there's some operator that sequences the side effects, the exact moment in which they are applied is not defined, so it's undefined exactly what values `i` would assume in the various points of the expression": I'm not sure I agree with this explanation. That explanation would explain why the behavior of `i = ++i + ++i` was *unspecified*, if it were; but the behavior is completely *undefined*, and that explanation doesn't really explain why. – ruakh Dec 20 '18 at 22:56
  • 2
    @ruakh It's undefined because it cannot be defined. It carries too much ambiguity. The question "Why is it undefined" is because you can't define it. – Nelson Dec 21 '18 at 05:46
  • 3
    @ruakh: ultimately, it's undefined because the standard says it's undefined; a good rationalization is that the standard left so much leeway for implementors to apply side-effects whenever and however they feel best that they went all the way and left it completely undefined to allow for any possible optimization (say, store half a value at some moment and the other half later, generating a trap representation in the meantime). – Matteo Italia Dec 21 '18 at 06:59
23

This

++*Ptr++;

doesn't cause U.B and is evaluated as ++(*(Ptr++))

  • ptr++; /* address post incremented i.e doesn't change here itself */
  • *ptr; /* dereference same address i.e value at location where ptr earlier pointed i.e 6 */
  • ++*ptr; /* value changed where ptr pointed i.e Ar[0] becomes 7 */

Note that post increments Ptr++ evaluated as

  • Ptr; /* Ptr doesn't change here itself in same expression */
  • Ptr = Ptr + 1; /* in next expression, Ptr considers the incremented one */
Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
Achal
  • 11,821
  • 2
  • 15
  • 37