Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In C and C++, is an expression using the comma operator like "a = b, ++a;" undefined?

Take these three snippets of C code:

1) a = b + a++
2) a = b + a; a++
3) a = b + a, a++

Everyone knows that example 1 is a Very Bad Thing, and clearly invokes undefined behavior. Example 2 has no problems. My question is regarding example 3. Does the comma operator work like a semicolon in this kind of expression? Are 2 and 3 equivalent or is 3 just as undefined as 1?

Specifically I was considering this regarding something like free(foo), foo = bar. This is basically the same problem as above. Can I be sure that foo is freed before it's reassigned, or is this a clear sequence point problem?

I am aware that both examples are largely pointless and it makes far more sense to just use a semicolon and be done with it. I'm just asking out of curiosity.

like image 231
Roflcopter4 Avatar asked May 03 '18 03:05

Roflcopter4


2 Answers

Case 3 is well defined.

First, let's look at how the expression is parsed:

a = b + a, a++

The comma operator , has the lowest precedence, followed by the assignment operator =, the addition operator + and the postincrement operator ++. So with the implicit parenthesis it is parsed as:

(a = (b + a)), (a++)

From here, section 6.5.17 of the C standard regarding the comma operator , says the following:

2 The left operand of a comma operator is evaluated as a void expression; there is a sequence point between its evaluation and that of the right operand. Then the right operand is evaluated; the result has its type and value

Section 5.14 p1 of the C++11 standard has similar language:

A pair of expressions separated by a comma is evaluated left-to-right; the left expression is a discarded- value expression. Every value computation and side effect associated with the left expression is sequenced before every value computation and side effect associated with the right expression. The type and value of the result are the type and value of the right operand; the result is of the same value category as its right operand, and is a bit-field if its right operand is a glvalue and a bit-field.

Because of the sequence point, a = b + a is guaranteed to be fully evaluated before a++ in the expression a = b + a, a++.

Regarding free(foo), foo = bar, this also guarantees that foo is free'ed before a new value is assigned.

like image 97
dbush Avatar answered Nov 16 '22 17:11

dbush


a = b + a, a++; is well-defined, but a = (b + a, a++); can be undefined.

First of all, the operator precedence makes the expression equivalent to (a = (b+a)), a++;, where + has the highest precedence, followed by =, followed by ,. The comma operator includes a sequence point between the evaluation of its left and right operand. So the code is, uninterestingly, completely equivalent to:

a = b + a;
a++;

Which is of course well-defined.


Had we instead written a = (b + a, a++);, then the sequence point in the comma operator wouldn't save the day. Because then the expression would have been equivalent to

(void)(b + a);
a = a++;
  • In C and C++14 or older, a = a++ is unsequenced , (see C11 6.5.16/3). Meaning this is undefined behavior (Per C11 6.5/2). Note that C++11 and C++14 were badly formulated and ambiguous.
  • In C++17 or later, the operands of the = operator are sequenced right to left and this is still well-defined.

All of this assuming no C++ operator overloading takes place. In that case, the parameters to the overloaded operator function will be evaluated, a sequence point takes place before the function is called, and what happens from there depends on the internals of that function.

like image 45
Lundin Avatar answered Nov 16 '22 15:11

Lundin