tags:

views:

296

answers:

6

Hi! This was a question in our old C++ exam. This code is driving me crazy, could anyone explain what it does and - especially - why?

int arr[3]={10,20,30};
int *arrp = new int;

(*(arr+1)+=3)+=5;
(arrp=&arr[0])++;

std::cout<<*arrp;
+5  A: 
(*(arr+1)+=3)+=5;

arr + 1 - element with index 1
*(arr + 1) - value of this element
(arr + 1) += 3 - increase by 3
(
(arr+1)+=3)+=5 - increase by 5;

so arr[1] == 28

(arrp=&arr[0])++;

arr[0] - value of element 0
&arr[0] - address of element 0
arrp=&arr[0] - setting arrp to point to elem 0
(arrp=&arr[0])++ - set arr to point to elem 1

result: 28

Andrey
Identical to my thought.
Pindatjuh
But there is no sequence point between the `+= 3` and the `+= 5` and the both operate on the same object?
Charles Bailey
@Charles: pretty sure it's valid. Otherwise there'd be little point in the += operator (or any assignment operator) having a non-void return type. But looking through the standard I can't find anything to clearly support this
jalf
@jalf: The point of it returning an lvalue is so that you can take its address or call a function on it (implying a separate sequence point). Writing to it again is definitely not valid (IMHO), but there are other things that you can do with an lvalue.
Charles Bailey
+5  A: 

This line:

(*(arr+1)+=3)+=5; 

produces the same result as this (see footnote):

arr[1] += 3;
arr[1] += 5;

This line:

(arrp=&arr[0])++;   

produces the same result as this (see footnote):

int* arrp = arr+1;

So this line:

std::cout<<*arrp

prints out 28.

But this code leaks memory because int *arrp = new int; allocates a new int on the heap which will be lost on assignment by (arrp=&arr[0])++;

Footnote: Of course I'm assuming an absence of weirdness.

Edit: Apparently some of the lines in fact lead to undefined behavior, due to C++ Standard 5/4. So this really is a crappy exam question.

In silico
`(*(arr+1)+=3)+=5;` is not identical to `arr[1] += 3; arr[1] += 5;` as you've introduced a sequence point in the second that isn't present in the first.
Charles Bailey
Right, I refined my post.
In silico
How would the operators be overloaded when none of the operands are user-defined types? What about the potential for undefined behavior in the original expressions?
Charles Bailey
I thought in this case the innermost expression is evaluated first (`arr+1`), then outwards due to the parentheses?
In silico
@In silico: Yes, that is how the expression must be evaluated but the two writes which are side-effects of the `+=` aren't separated from each other by a sequence point so the behavior is undefined, unless you have an argument as to why it isn't?
Charles Bailey
+1 for noting the memory leak.
andand
+1  A: 
int arr[3]={10,20,30}; // obvious?
int *arrp = new int; // allocated memory for an int

(*(arr+1)+=3)+=5; // (1)
(arrp=&arr[0])++; // (2)

std::cout<<*arrp; // (3)

(1)

*(arr+1) is the same as arr[1], which means that *(arr+1)+=3 will increase arr[1] by 3, so arr[1] == 23 now.

(*(arr+1)+=3)+=5 means arr[1] is increased by another 5, so it will be 28 now.

(2)

arrp will pont to the address of the first element of arr (arr[0]). The pointer arrp will then be incremented, thus it will point to the second element after the entire statement is executed.

(3)

Prints what arrp points to: the second element of arr, meaning 28.

IVlad
+1  A: 

Well, remember that arrays can be interpreted as pointers

int arr[3]={10,20,30};
int *arrp = new int;

creates an array arr of three integers and an int pointer that gets assigned with a freshly allocated value.

Since assignment operators return a reference to the value that has been assigned in order to allow multi-assignment,

(*(arr+1)+=3)+=5;

is equivalent to

*(arr+1)+=3;
*(arr+1)+=5;

*(arr + 1) refers to the first element of the array arr, therefore arr[1] is effectively increased by eight.

(arrp=&arr[0])++; assigns the address of the first array element to arrp and afterward increments this pointer which now points to the second element (arr[1] again).

By dereferencing it in std::cout<<*arrp, you output arr[1] which now holds the value 20 + 3 + 5 = 28.

So the code prints 28 (and furthermore creates a memory-leak since the new int initially assigned to arrp never gets deleted)

Dario
+10  A: 

This statement writes to the object *(arr+1) twice without an intervening sequence point so has undefined behavior.

(*(arr+1)+=3)+=5;

This statement writes to the object arrp twice without an intervening sequence point so has undefined behavior.

(arrp=&arr[0])++;

The code could result in anything happening.

Reference: ISO/IEC 14882:2003 5 [expr]/4: "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."

Charles Bailey
A: 

I'll try to answer you by rewriting the code in a simpler way.

int arr[3]={10,20,30};
int *arrp = new int;

(*(arr+1)+=3)+=5;
(arrp=&arr[0])++;

std::cout<<*arrp;

=== equals ===

int arr[3]={10,20,30};//create array of 3 elements and assign them
int *arrp = new int;//create an integer pointer and allocate an int to it(useless)

//(*(arr+1)+=3)+=5;
arr[1] = arr[1] + 3;//arr[1] == arr+1 because it is incrementing the arr[0] pointer
arr[1] = arr[1] + 5;

//(arrp=&arr[0])++;
arrp = &arr[0];//point the integer pointer to the first element in arr[]
arrp++;//increment the array pointer, so this really is now pointing to arr[1]

std::cout<<*arrp;//just print out the value, which is arr[1]

I am assuming you understand pointers and basic c.

Michael Ozeryansky