Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C Pointers from Past Paper [duplicate]

Tags:

c

pointers

I have another C pointers question.

Consider executing the following program:

int x[5] = {0,3,5,7,9};
int* y = &x[2];
*(y+2) = *(y--);

What values does the array x hold afterwards?

What the hell is going on with y--? I know how *(y+2) works, and understand the rest, but not how y-- ties in with the rest.

Also, the answer given is {0, 3, 5, 5, 9}.

like image 757
cmcsorley17 Avatar asked Dec 18 '14 13:12

cmcsorley17


2 Answers

There's no sequence point between y-- and y + 2 in *(y+2) = *(y--);, so whether y + 2 refers to &x[4] or &x[3] is unspecified. Depending on how your compiler does things, you can either get 0 3 5 5 9 or 0 3 5 7 5.

What it means that there is no sequence point between the two expressions is, in a nutshell, that it is not specified whether the side effects of one operation (--y in this case) have been applied by the time the other (y - 2) is evaluated. You can read more about sequence points here.

ISO/IEC 9899:201x

6.5 Expressions

p2: 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. If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings.

like image 72
Wintermute Avatar answered Oct 21 '22 14:10

Wintermute


You should not trust the answers given by your professor in this case.

Expanding on Wintermute's answer a bit...

The problem is with the statement

*(y+2) = *(y--);

The expression y-- evaluates to the current value of y, and as a side effect decrements the variable. For example:

int a = 10;  
int b;

b = a--;

After the above expression has been evaluated, b will have the value 10 and a will have the value 9.

However, the C language does not require that the side effect be applied immediately after the expression has been evaluated, only that it be applied before the next sequence point (which in this case is at the end of the statement). Neither does it require that expressions be evaluated from left to right (with a few exceptions). Thus, it's not guaranteed that the value of y in y+2 represents the value of y before or after the decrement operation.

The C language standard explicitly calls operations like this out as undefined behavior, meaning that the compiler is free to handle the situation in any way it wants to. The result will vary based on the compiler, compiler settings, and even the surrounding code, and any answer will be equally correct as far as the language definition is concerned.

In order to make this well-defined and give the same result, you would need to decrement y before the assignment statement:

y--;
*(y+2) = *y; 

This is consistently one of the most misunderstood and mis-taught aspects of the C language. If your professor is expecting this particular result to be well-defined, then he doesn't know the language as well as he thinks he does. Then again, he's not unique in that respect.

Repeating and expanding on the snippet from the C 2011 draft standard that Wintermute posted:

6.5 Expressions
...
2 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. If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings.84)

3 The grouping of operators and operands is indicated by the syntax.85)Except as specified later, side effects and value computations of subexpressions are unsequenced.86)
84) This paragraph renders undefined statement expressions such as
    i = ++i + 1;
    a[i++] = i;
while allowing
    i = i + 1;
    a[i] = i;

85) The syntax specifies the precedence of operators in the evaluation of an expression, which is the same as the order of the major subclauses of this subclause, highest precedence first. Thus, for example, the expressions allowed as the operands of the binary + operator (6.5.6) are those expressions defined in 6.5.1 through 6.5.6. The exceptions are cast expressions (6.5.4) as operands of unary operators (6.5.3), and an operand contained between any of the following pairs of operators: grouping parentheses () (6.5.1), subscripting brackets [] (6.5.2.1), function-call parentheses () (6.5.2.2), and the conditional operator ? : (6.5.15). Within each major subclause, the operators have the same precedence. Left- or right-associativity is indicated in each subclause by the syntax for the expressions discussed therein.

86) 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.

Emphasis added. Note that this has been true since the C89 standard, although the wording has changed a bit since then.

"Unsequenced" simply means it's not guaranteed that one operation is completed before the other. The assignment operator does not introduce a sequence point, so it's not guaranteed that the LHS of the expression is evaluated before the RHS.

Now for the hard bit - your professor obviously expects a specific behavior for these kinds of expressions. If he gives a test or a quiz that asks what the result of something like a[i] = i--; will be, he's probably not going to accept an answer of "the behavior is undefined", at least not on its own. You might want to discuss the answers Wintermute and I have given with him, along with the sections of the standard quoted above.

like image 27
John Bode Avatar answered Oct 21 '22 14:10

John Bode