15

In the following code excerpt from a larger piece of code presented

void func(int* usedNum, int wher) {
    *usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1; 
}

int main(void) {
   int a = 11, b = 2; 
   func(&a, b); 
}

a warning is emitted

 warning: operation on '* usedNum' may be undefined [-Wsequence-point]
 *usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1; 

Is there a problem with the code?

My source of doubt was this and the part where it says

The sequence points in the logical expressions such as && and || and ternary operator ?: and the comma operator mean that the left hand side operand is evaluated before the right hand side operand. These few operands are the only operands in C++ that introduce sequence points.

tl;dr

For those that find torturing to read through the comments: The initial question was not properly posed and it would be unfair to create misconceptions. My view on the topic had two sides

  1. The ternary operator does not mess up (in an unexpected way) the sequence points (which holds, the two branches are sequenced in every version of C,C++ - see the link provided)

  2. Is x = ++x the problem? As seen in the coliru link, we compile for c++14. There the operation is well defined (references on the comments), but older versions of c++ and c view this as undefined. So why is there a warning?

Answers focus both in C and C++; this is a good link. Lastly the C tag was there initially (my bad) and can't be removed because existing upvoted answers refer to it

Community
  • 1
  • 1
Nikos Athanasiou
  • 29,616
  • 15
  • 87
  • 153
  • 5
    Isn't it the same as saying `x = ++x`? So yes, potential UB if the truthy branch is followed. – juanchopanza Feb 10 '15 at 09:07
  • @juanchopanza isn't it equivalent as saying `x = (++x)` ? which according to [this](http://stackoverflow.com/a/4176333/2567683) is well defined? (Look at the part where it says `i = (++i,i++,i) // well defined`) – Nikos Athanasiou Feb 10 '15 at 09:10
  • The comma operator introduces sequence points. The code in your question doesn't have comma operators. – juanchopanza Feb 10 '15 at 09:12
  • @juanchopanza The ternary operation, better yet the branches of the ternary operator, are not sequenced? – Nikos Athanasiou Feb 10 '15 at 09:13
  • 1
    This looks like C++, so why the C tag? – T.C. Feb 10 '15 at 09:19
  • 4
    Yes, that means that `a` is evaluated before `b` or `c` in `a ? b : c`, but doesn't mean the whole thing is sequenced before the LHS of an assignment. – juanchopanza Feb 10 '15 at 09:20
  • I added the C tag because the answers are for C. If you want a C++ specific question, you should tag only as C++. I would even consider removing the C++ tag. – juanchopanza Feb 10 '15 at 09:30
  • I'm too lazy to write a C++ answer. This is well-defined in C++11 and g++'s producing a warning is a bug. See http://stackoverflow.com/questions/17400137/order-of-evaluation-and-undefined-behaviour – T.C. Feb 10 '15 at 09:36
  • The article you link to refers to C++98 (since it was written more than a decade ago). The sequencing rules changed considerably in C++11, and I think this monstrosity is well-defined in modern dialects of C++. I've no idea about (modern) C. Please choose a language - despite their common syntax, C and C++ are quite different. – Mike Seymour Feb 10 '15 at 09:48
  • @MikeSeymour To be fair, OP removed the C tag, but this was after two C answers had been provided, viewed and up-voted. I re-introduced the C tag for that reason, since removing it is essentially changing the question after it has been answered. But I completely agree that it should only have had one language tag from the outset. – juanchopanza Feb 10 '15 at 09:51
  • @T.C. I don't see this warning as a bug, as there is enough debate on this, to make change the code so as to avoid having to have the debate. – Ian Ringrose Feb 10 '15 at 13:01
  • @NikosAthanasiou I took the liberty to make the code valid C and C++. The relevant semantics don't change. – juanchopanza Feb 10 '15 at 13:40

2 Answers2

17

When the condition is true, it is the equivalent of saying x = ++x. In C, and versions of C++ prior to C++11, this constitutes a modification and a read of x without an intervening sequence point and therefore is undefined behaviour if the truthy branch is followed. From C++11 onwards, x = ++x is sequenced and well defined.


Edit To clarify some issues from comments.

1) this would be well defined in all C and C++ standards:

x = (++x, x); // RHS evaluates to x after increment

because the expression in the parentheses involves the comma operator, which introduce a sequence point between the evaluation of its operands. So the whole expression on the RHS evaluates to x after an increment. But the code in your question does not involve the comma operator.

2) The ternary operator introduces a sequence point

It is a sequence point between the condition and the two branches. But this doesn't introduce a sequence point between either branch and the assignment.

Community
  • 1
  • 1
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • isn't it equivalent as saying `x = (++x)` ? which according to [this](http://stackoverflow.com/a/4176333/2567683) is well defined? – Nikos Athanasiou Feb 10 '15 at 09:09
  • @NikosAthanasiou Exactly. – juanchopanza Feb 10 '15 at 09:09
  • 2
    I'm fairly sure `x = ++x;` is well-defined in C++11. – T.C. Feb 10 '15 at 09:16
  • @T.C. This was originally tagged C. I answered with C in mind. – juanchopanza Feb 10 '15 at 09:28
  • @juanchopanza Last time I checked, `int a(1), b(2);` isn't valid C. – T.C. Feb 10 '15 at 09:31
  • 2
    @T.C. I read the relevant part of the question and the C tag. Both answers are C. OP's problem to tag things correctly. – juanchopanza Feb 10 '15 at 09:32
  • @T.C. I also think that assignment is sequenced after preincrementing, but juannchopanza is correct that there was a C tag as well (I removed it because the rules are conflicting for the two languages) – Nikos Athanasiou Feb 10 '15 at 09:32
  • In regards to C++11, [CWG 637](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#637) –  Feb 10 '15 at 09:33
  • To be (a bit) fair even my first comment said what @T.C. said, but you are right I messed the question tagwise. Got my answer but don't know how much help it'll be for others (maybe I'll make a dissambiguation post much later when I have the time). Thnx for your help – Nikos Athanasiou Feb 10 '15 at 09:38
  • 1
    @remyabel the `+1` is significant there; in C++11 `i = ++i;` is UB but `i = ++i + 1;` is OK – M.M Feb 10 '15 at 09:38
  • @T.C. Anyway, do you have a good reference for `x = ++x` in C++11? I didn't follow the sequencing rule changes that closely. Never mind, found it. – juanchopanza Feb 10 '15 at 09:40
  • @MattMcNabb How is it UB? – T.C. Feb 10 '15 at 09:44
  • @NikosAthanasiou OK, I have tried to make my answer 2-language, multi-standards compliant :-) – juanchopanza Feb 10 '15 at 09:45
  • @TC this is too big a discussion for comments. AndreyT posted a good analysis somewhere on here. – M.M Feb 10 '15 at 09:54
  • @MattMcNabb [Here it is](http://stackoverflow.com/a/3619370/3920237). Though the answer was posted in 2010. –  Feb 10 '15 at 10:35
  • @MattMcNabb `x= ++x` and `x = x++` are different. And that answer doesn't discuss C++11. – T.C. Feb 10 '15 at 11:16
  • @remyabel; That answer is outdated now. – haccks Feb 10 '15 at 11:44
  • 2
    @T.C.; `x = ++x;` is well defined in C11 too. – haccks Feb 10 '15 at 11:44
  • @remyabel actually [this one](http://stackoverflow.com/a/14005575/1505939) is what I was referring to. The key point is that the in the expression `++i + 1`, the result of `++i` must undergo lvalue-to-rvalue conversion , which forces the side-effect of `++i` to be sequenced-before the assignment. However in `i = ++i;` , there is no lvalue-to-rvalue conversion needed on the result of `++i` because it is already known. – M.M Feb 11 '15 at 01:07
  • @MattMcNabb Whether there is an lvalue-to-rvalue conversion on the RHS of simple assignment (I would argue there is; in `x = y;` you obviously need to read `y`) doesn't matter; the key here is that the rules in the standard explicitly provides that the assignment performed by `=` is sequenced after the value computation of the operands, including `++x`, and the value computation of the expression `++x` is sequenced after the assignment performed by that expression ([expr.ass]/p1). Hence there's a chain of sequenced-befores ordering the assignments. [...] – T.C. Feb 11 '15 at 18:55
  • @MattMcNabb What AndreyT said w/r/t the lvalue-to-rvalue conversion is perhaps a reason for the sequencing rules to be specified this way, but the rules as specified do not depend on the existence of lvalue-to-rvalue conversion on the result of the assignment. "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." – T.C. Feb 11 '15 at 18:56
  • @juanchopanza In my discussion with @ haccks below, we seem to have agreed that this remains UB in C11, which doesn't guarantee that the assignment is sequenced before the value computation of the assignment expression, rendering the two assignments unsequenced. – T.C. Feb 11 '15 at 18:59
  • @T.C. Yes, I can see that. I am playing catch-up. I will look into this carefully and edit the answer if appropriate. – juanchopanza Feb 11 '15 at 19:01
7

The warning you are getting is probably due to the fact that you are compiling your code in c++03 mode or older. In C99 and C++03, expression

x = ++x;

invokes undefined behavior. The reason is that between two sequence points an object can't modify more than once.

This rule is changed in C11 and C++11. According to C11, the rule is as follows:

C11:6.5 Expressions:

If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

When *usedNum + 1 > wher will be true, then

*usedNum = *usedNum + 1 > wher ? ++(*usedNum) : wher + 1;   

would be equivalent to

*usedNum = ++(*usedNum);  

and according to new rule this is well defined in C++11 this is because the side effect by pre ++ is sequenced before the side effect by = operator. Read this answer for more detailed explanation.

But the same expression *usedNum = ++(*usedNum); invokes undefined behavior in C11. The reason is that there is no guarantee that side effect by = operator is sequenced after the side effect of pre ++ operator.


Note: In the expression

a = x++ ? x++ : 0; 

there is sequence point after the first x++ and hence behavior is well defined. Same is true for

x = (++x, x);  

because there is a sequence point between the evaluation of left and right operand and hence side effect is sequenced.

Community
  • 1
  • 1
haccks
  • 104,019
  • 25
  • 176
  • 264
  • 1
    @NikosAthanasiou Those parentheses make no difference. – juanchopanza Feb 10 '15 at 09:15
  • I'm not sure I'm seeing anything in the C standard regarding the relative sequencing between the value computation of the expression `x += 1` and the side effect. Without this, there's no established sequence between the two assignments performed by the expression. – T.C. Feb 10 '15 at 12:28
  • @T.C.; `x += 1` is sequenced before assignment to `x` in `x = (x += 1)`. – haccks Feb 10 '15 at 12:35
  • @haccks No, the *value computation* of `x += 1` is sequenced before the assignment to `x` in `x = (x += 1)`. That says nothing about the *side effect* of `x += 1`. – T.C. Feb 10 '15 at 12:37
  • @T.C. Read Section 6.5.16: *The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands.* – haccks Feb 10 '15 at 12:38
  • @haccks sure, but that says that the side effect of `=` is sequenced after the *value computation* of `x += 1`; and similarly the side effect of `+=` is sequenced after the value computation of `x + 1` etc; but it doesn't say that the side effect of `+=` is sequenced before the value computation of `x += 1`, and without that you can't say that the two side effects are sequenced. – T.C. Feb 10 '15 at 12:43
  • @T.C.; Why should side effect of `+=` is sequenced before the value computation of `x += 1`? – haccks Feb 10 '15 at 13:21
  • @haccks If the side effect of `+=` is not sequenced before the value computation of `x += 1`, then there's no sequencing relationship between the side effect of `+=` and the side effect of `=`. – T.C. Feb 10 '15 at 15:41
  • @T.C.; Read 6.5 p1: *The value computations of the operands of an operator are sequenced before the value computation of the result of the operator.* – haccks Feb 10 '15 at 16:31
  • @haccks Sure, but that has nothing to do with how the *side effects* are sequenced relative to each other. – T.C. Feb 10 '15 at 16:38
  • @T.C.; For `x = (x = x+1)` (expandind `x = ++x`), `x+1` is evaluated and side effect to second `x` from LHS will take place. After that side effect to first `x` from LHS will take place. That means first side effect is sequenced before the second one. – haccks Feb 10 '15 at 16:45
  • @haccks The guarantees are that 1) the *side effect* to the second `x` is sequenced after the *value computation* of `x + 1` and 2) the *side effect* to the first `x` is sequenced after the *value computation* of `x = x + 1`. But there's no guarantee that the *side effect* of `x = x + 1` is also sequenced before the assignment to the first `x`. Note that footnote 84, in 6.5, explicitly labels `i = ++i + 1;` as UB. – T.C. Feb 10 '15 at 16:53
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/70650/discussion-between-t-c-and-haccks). – T.C. Feb 10 '15 at 17:15