Do parentheses force order of evaluation and make an undefined expression defined?
Asked Answered
S

2

15

I was just going though my text book when I came across this question:

What would be the value of a after the following expression? Assume the initial value of a = 5. Mention the steps.

a+=(a++)+(++a)

At first I thought this is undefined behaviour because a has been modified more than once. Then I read the question and it said "Mention the steps" so I probably thought this question is right.

  • Does applying parentheses make an undefined behaviour defined?
  • Is a sequence point created after evaluating a parentheses expression?
  • If it is defined,how do the parentheses matter since ++ and () have the same precedence?
Sylvanite answered 2/4, 2014 at 16:26 Comment(6)
Your best bet is to write sensible code that is both readable and maintainable. Thus avoid this line of thought in the first place.Jacquiline
Does your textbook purport to teach "C/C++"? Just curious.Blowsy
@n.m. My Textbook teaches C :)Sylvanite
That link gets it wrong, Akshay. It reasons that since preincrement has higher precedence than postincrement when applied to a single operand, it also gets evaluated first when applied to distinct operands. That reasoning is faulty, though. There's no basis for it.Singapore
possible duplicate of Undefined Behavior and Sequence PointsBlowsy
Alas, many, many textbooks and websites purport to teach C and C++ (and other languages) but get a lot of very fundamental things wrong. It really is a buyer-beware industry, targeting buyers who couldn’t possibly know any better.Misspeak
R
16

No, applying parentheses doesn't make it a defined behaviour. It's still undefined. The C99 standard §6.5 ¶2 says

Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.

Putting a sub-expression in parentheses may force the order of evaluation of sub-expressions but it does not create a sequence point. Therefore, it does not guarantee when the side effects of the sub-expressions, if they produce any, will take place. Quoting the C99 standard again §5.1.2.3¶2

Evaluation of an expression may produce side effects. 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.

For the sake of completeness, following are sequence points laid down by the C99 standard in Annex C.

  1. The call to a function, after the arguments have been evaluated.

  2. The end of the first operand of the following operators: logical AND &&; logical OR ||; conditional ?; comma ,.

  3. The end of a full declarator.

  4. The end of a full expression; the expression in an expression statement; the controlling expression of a selection statement (if or switch); the controlling expression of a while or do statement; each of the expressions of a for statement; the expression in a return statement.

  5. Immediately before a library function returns.

  6. After the actions associated with each formatted input/output function conversion specifier.

  7. Immediately before and immediately after each call to a comparison function, and also between any call to a comparison function and any movement of the objects passed as arguments to that call.

Ruthenian answered 2/4, 2014 at 16:30 Comment(6)
then how would you explain what's being thought on this page.Sylvanite
By saying that was written by someone who doesn't know what they're talking about.Partida
Its sad cause if you google "Where to learn C" that site is the fourth link in the search results.Sylvanite
@AkshayLAradhya: I advise using critical judgement. 1. Not everything that is written is true (x-ref help, but do not prove anything) and 2. The goal of any company is first and foremost making money (and being accurate is expensive, for possibly little benefits)Mindoro
Given that computer science is based on mathematics, its amazing the C language approved that foolishness. Just about anywhere else parenthesis would force order of evaluations and produce well defined results. How parenthesized variables could somehow be undefined is befuddling.Charkha
@Charkha That's because, both in C and in mathematics, parentheses do not mean, "do everything inside first". Consider the mathematical expression f(x) × (g(y) + h(z)). Which function will be evaluated first, f, g, or h? See also this answer.Torietorii
R
5

Adding parenthesis does not create a sequence point and in the more modern standards it does not create a sequenced before relationship with respect to side effects which is the problem with the expression that you have unless noted the rest of this will be with respect to C++11. Parenthesis are a primary expression covered in section 5.1 Primary expressions, which has the following grammar (emphasis mine going forward):

primary-expression:
  literal
  this
  ( expression )
  [...]

and in paragraph 6 it says:

A parenthesized expression is a primary expression whose type and value are identical to those of the enclosed expression. The presence of parentheses does not affect whether the expression is an lvalue. The parenthesized expression can be used in exactly the same contexts as those where the enclosed expression can be used, and with the same meaning, except as otherwise indicated.

The postfix ++ is problematic since we can not determine when the side effect of updating a will happen pre C++11 and in C this applies to both the postfix ++ and prefix ++ operations. With respect to how undefined behavior changed for prefix ++ in C++11 see Assignment operator sequencing in C11 expressions.

The += operation is problematic since:

[...]E1 op = E2 is equivalent to E1 = E1 op E2 except that E1 is evaluated only once[...]

So in C++11 the following went from undefined to defined:

a = ++a + 1 ;

but this remains undefined:

a = a++ + 1 ;

and both of the above are undefined pre C++11 and in both C99 and C11.

From the draft C++11 standard section 1.9 Program execution paragraph 15 says:

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced. [ Note: In an expression that is evaluated more than once during the execution of a program, unsequenced and indeterminately sequenced evaluations of its subexpressions need not be performed consistently in different evaluations. —end note ] The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

Rick answered 2/4, 2014 at 16:32 Comment(4)
Your final sentence seems to suggest that if the code used the prefix increment operator (for a += ++a + ++a) then C++11 would consider that statement defined. Is that really true? What is the final value of a in that case?Singapore
@RobKennedy I added a link to a question I just answered recently in C++11 prefix ++ returns an lvalue which means that when you read the value out it forces an lvalue to revalue conversion which forces a sequenced before relationship with repsect to the side effect.Rick
So what you are saying is that the expression a+=(a++)+(++a) is valid ?Sylvanite
@AkshayLAradhya see my update, it is not valid both the postfix ++ and the += create undefined behavior.Rick

© 2022 - 2024 — McMap. All rights reserved.