Is the Comma in a Variable List a Sequence Point

Is the comma in a variable list a sequence point?

I believe behavior is well-defined because of 8[dcl.decl]/3

Each init-declarator in a declaration is analyzed separately as if it was in a declaration by itself.

Which is even additionally explained in a footnote as

A declaration with several declarators is usually equivalent to the corresponding sequence of declarations each with a single
declarator. That is

T D1, D2, ... Dn;

is usually equvalent to

T D1; T D2; ... T Dn;

What does the comma operator , do?

The expression:

(expression1,  expression2)

First expression1 is evaluated, then expression2 is evaluated, and the value of expression2 is returned for the whole expression.

Are there sequence-point issues with statements like int a=4,*ptr=&a; or x+=4,y=x*2;?

The comma that separates function arguments in a function call is not a comma operator - it's just punctuation that happens to be spelled in the same way as the comma operator. There's no sequence point between the evaluation of different function arguments, so that's why you get UB in that case.

On the other hand, in your expression:

x+=4,y=x*2;

the comma here is a comma operator, which introduces a sequence point; there's no UB.

In a declaration, the comma between declarators is also not a comma operator; however, the end of a full declarator (a declarator not part of another declarator) does introduce a sequence point, so a declaration like:

int a = 2, b = a + 1;

is not UB.

Is the order of evaluation with comma operator & assignment in C predictable?

The order is defined, because there is a sequence point between them. See ISO/IEC 9899 6.5.17:

The left operand of a comma operator is evaluated as a void
expression; there is a sequence point after its evaluation. Then
the right operand is evaluated; the result has its type and value. 95)
If an attempt is made to modify the result of a comma operator or to
access it after the next sequence point, the behavior is undefined.

They then give an explicit example:

In the function call

f(a, (t=3, t+2), c)
the function has three
arguments, the second of which has the value 5.

I'm not entirely sure why CppCheck is flagging it.

What is a comma separated set of assignments?

A comma operator is a sequence point : each comma separated expression are evaluated from left to right. The result has the type and value of the right operand. Functionally, your example is equivalent to (the much more readable ?) :

else
{
*pbuf++ = '%';
*pbuf++ = to_hex(*pstr >> 4);
*pbuf++ = to_hex(*pstr & 15);
}

Here is another example that the standard provides for the comma operator (6.5.17) :

In the function call

f(a, (t=3, t+2), c)

the function has three arguments, the
second of which has the value 5.

Is the order of assignment in a list of initialized variables undefined?

It's well-defined:

8. Declarators: [dcl.decl]

3) Each init-declarator in a declaration is analyzed separately as if it was in a declaration by itself.

And the note:

90) A declaration with several declarators is usually equivalent to
the corresponding sequence of declarations each with a single
declarator. That is

T D1, D2, ... Dn;

is usually equvalent to

T D1; T D2; ... T Dn;

where T is a decl-specifier-seq and each Di is an
init-declarator.

For completness (because the note says usually):

The exception occurs when a name introduced by one of the declarators
hides a type name used by the dcl-specifiers, so that when the same
dcl-specifiers are used in a subsequent declaration, they do not have
the same meaning, as in struct S { ... }; S S, T; // declare two
instances of struct Swhich is not equivalent tostruct S { ... }; S
S; S T; // error`

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

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.

C - Multiple assignments to same variable in one line

Is it valid to assign something to K multiple times?

That's perfectly valid C macro. A comma operator , is used here.

Using , operator you can assign a value to a variable multiple times.

e.g.K = 20, K = 30; This will assign 30 to K overwriting the previous value of 20.


I thought it's invalid to change a variable more than one time in one statement.

Yes it leads to undefined behavior if we try to modify a variable more than once in a same C statement but here first , is a sequence point.

So it's guaranteed that we will be modifying the K second time (K = 30) only when all the side effects of first assignment (K = 20) have taken place.

Undefined behavior and sequence points

C++98 and C++03

This answer is for the older versions of the C++ standard. The C++11 and C++14 versions of the standard do not formally contain 'sequence points'; operations are 'sequenced before' or 'unsequenced' or 'indeterminately sequenced' instead. The net effect is essentially the same, but the terminology is different.


Disclaimer : Okay. This answer is a bit long. So have patience while reading it. If you already know these things, reading them again won't make you crazy.

Pre-requisites : An elementary knowledge of C++ Standard



What are Sequence Points?

The Standard says

At certain specified points in the execution sequence called sequence points, all side effects of previous evaluations
shall be complete and no side effects of subsequent evaluations shall have taken place. (§1.9/7)

Side effects? What are side effects?

Evaluation of an expression produces something and if in addition there is a change in the state of the execution environment it is said that the expression (its evaluation) has some side effect(s).

For example:

int x = y++; //where y is also an int

In addition to the initialization operation the value of y gets changed due to the side effect of ++ operator.

So far so good. Moving on to sequence points. An alternation definition of seq-points given by the comp.lang.c author Steve Summit:

Sequence point is a point in time at which the dust has settled and all side effects which have been seen so far are guaranteed to be complete.



What are the common sequence points listed in the C++ Standard?

Those are:

  • at the end of the evaluation of full expression (§1.9/16) (A full-expression is an expression that is not a subexpression of another expression.)1

    Example :

    int a = 5; // ; is a sequence point here
  • in the evaluation of each of the following expressions after the evaluation of the first expression (§1.9/18) 2

    • a && b (§5.14)
    • a || b (§5.15)
    • a ? b : c (§5.16)
    • a , b (§5.18) (here a , b is a comma operator; in func(a,a++) , is not a comma operator, it's merely a separator between the arguments a and a++. Thus the behaviour is undefined in that case (if a is considered to be a primitive type))
  • at a function call (whether or not the function is inline), after the evaluation of all function arguments (if any) which
    takes place before execution of any expressions or statements in the function body (§1.9/17).

1 : Note : the evaluation of a full-expression can include the evaluation of subexpressions that are not lexically
part of the full-expression. For example, subexpressions involved in evaluating default argument expressions (8.3.6) are considered to be created in the expression that calls the function, not the expression that defines the default argument

2 : The operators indicated are the built-in operators, as described in clause 5. When one of these operators is overloaded (clause 13) in a valid context, thus designating a user-defined operator function, the expression designates a function invocation and the operands form an argument list, without an implied sequence point between them.



What is Undefined Behaviour?

The Standard defines Undefined Behaviour in Section §1.3.12 as

behavior, such as might arise upon use of an erroneous program construct or erroneous data, for which this International Standard imposes no requirements 3.

Undefined behavior may also be expected when this
International Standard omits the description of any explicit definition of behavior.

3 : permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or with-
out the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

In short, undefined behaviour means anything can happen from daemons flying out of your nose to your girlfriend getting pregnant.



What is the relation between Undefined Behaviour and Sequence Points?

Before I get into that you must know the difference(s) between Undefined Behaviour, Unspecified Behaviour and Implementation Defined Behaviour.

You must also know that the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified.

For example:

int x = 5, y = 6;

int z = x++ + y++; //it is unspecified whether x++ or y++ will be evaluated first.

Another example here.


Now the Standard in §5/4 says


    1. Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression.

What does it mean?

Informally it means that between two sequence points a variable must not be modified more than once.
In an expression statement, the next sequence point is usually at the terminating semicolon, and the previous sequence point is at the end of the previous statement. An expression may also contain intermediate sequence points.

From the above sentence the following expressions invoke Undefined Behaviour:

i++ * ++i;   // UB, i is modified more than once btw two SPs
i = ++i; // UB, same as above
++i = 2; // UB, same as above
i = ++i + 1; // UB, same as above
++++++i; // UB, parsed as (++(++(++i)))

i = (i, ++i, ++i); // UB, there's no SP between `++i` (right most) and assignment to `i` (`i` is modified more than once btw two SPs)

But the following expressions are fine:

i = (i, ++i, 1) + 1; // well defined (AFAIK)
i = (++i, i++, i); // well defined
int j = i;
j = (++i, i++, j*i); // well defined




    1. Furthermore, the prior value shall be accessed only to determine the value to be stored.

What does it mean? It means if an object is written to within a full expression, any and all accesses to it within the same expression must be directly involved in the computation of the value to be written.

For example in i = i + 1 all the access of i (in L.H.S and in R.H.S) are directly involved in computation of the value to be written. So it is fine.

This rule effectively constrains legal expressions to those in which the accesses demonstrably precede the modification.

Example 1:

std::printf("%d %d", i,++i); // invokes Undefined Behaviour because of Rule no 2

Example 2:

a[i] = i++ // or a[++i] = i or a[i++] = ++i etc

is disallowed because one of the accesses of i (the one in a[i]) has nothing to do with the value which ends up being stored in i (which happens over in i++), and so there's no good way to define--either for our understanding or the compiler's--whether the access should take place before or after the incremented value is stored. So the behaviour is undefined.

Example 3 :

int x = i + i++ ;// Similar to above

Follow up answer for C++11 here.

Are there sequence points in the expression a^=b^=a^=b, or is it undefined?

a ^= b ^= a ^= b; /*Here*/

It is undefined behavior.

You are modifying an object (a) more than once between two sequence points.

(C99, 6.5p2) "Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression.

Simple assignments as well as compound assignments don't introduce a sequence point. Here there is a sequence point before the expression statement expression and after the expression statement.

Sequence points are listed in Annex C (informative) of the c99 and c11 Standard.



Related Topics



Leave a reply



Submit