Hi guys,
I just came across this code fragment. The part in bold is the part of interest
1 2 3 4 5 6
void DLinkedList::add(DNode* v, const Elem& e) { // insert new node before v
DNode* u = new DNode; u -> value = e; // create a new node for e
u -> next = v; // link u in between v
u -> prev = v -> prev; // ...and v -> prev
v -> prev -> next = v -> prev = u;
}
DLinkedList is a Doubly Linked List. prev is a pointer to the previous node in the list, and next is a pointer to the next node in the list.
I want to confirm that the bold/italic statement is a parallel assignment - that is, u is simultaneously assigned to v's prev and v's (old) prev -> next.
No. There isn't such a thing as parallel assignment in a single thread.
Don't try to compress code like that.
This:
a = b = c;
is the same as this:
1 2
b = c;
a = b;
There's no benefit to smooshing it all on one line. Especially not if you're changing one of the values mid-statement. That code is broken as a result.
New lines and semicolons don't cost anything. Use them!
If your explanation is correct, then the last statement looks inaccurate.
The first three lines in the function body are clear:
- Allocate a node in memory, set its value to e
- Have its 'next' pointer point to v
- Have its 'prev' pointer point to v's 'prev' pointer
Then we get to the last statement. Going off your explanation:
- First 'u' is assigned to v's 'prev' pointer. Makes sense since the goal is to insert u in front of v.
- Then v's 'prev' pointer (which now equals u, according to your explanation) is assigned to v -> prev -> next, which means 'u' now points to itself.
If your explanation is correct, then the last statement looks inaccurate.
It is.
Perhaps the textbook made a mistake
It must have.
The exact behavior here is undefined. maybe it will work and maybe it won't.
The rule here is you can't use something in the same line that you modify it.
Consider this classic example:
1 2
int i = 0;
int j = ++i + i++;
What is j? C++ says it's undefined. Maybe you'll get 2, maybe 1, maybe 3, maybe something else entirely. The compiler is free to evaluate that any number of ways.
You have a similar problem here:
v -> prev -> next = v -> prev = u;
Maybe the compiler will extract prev->next before assigning prev. Or maybe it won't. There's no way to know. C++ doesn't say the compiler has to do it any particular way.
I think v -> prev -> next = v -> prev = u; statement is fine
I'm sure you're incorrect. The compiler could compile it to either of the following:
1 2 3
Node* p = v->prev;
v->prev = u;
p->next = u;
or
1 2
v->prev = u;
v->prev->next = u;
But as you can see, those do 2 very different things.
Or, the compiler might decide to do something completely different.
when there is increment and assignment at once, than we cannot be sure what will be the outcome of the final result.
You have the same problem here. v->prev is being both read and written at once. You can't know which one the compiler will do first because you didn't give it a sequence point (semicolon).
v->prev->next = v->prev = u; is bad because of the unknown order of evaluation. For the sake of a few more keystrokes you have something that works in a predicable manner.
Edit: u->prev->next = v->prev = u; would probably be okay.
@Grey Wolf: Wouldn't that be the same result in all compilers? I'll test in VS2008 and ideone.com (gcc 4.3.4). I always thought that assignment was evaluated from right to left as writeonsharma stated.
The result is wrong yes, but because the line was poorly constructed. If you write b->prev = b->prev->next = c; then you get the correct result because you first make b->prev (which is a) point to c, the element being inserted, and then you make b point backwards to c, which is also correct.
Yes, assignment is right associative that is not in question here.
In v->prev->next = v->prev = u; is v->prev->next evaluated before or after u is assigned to v->prev? This is the bit that is undefined behavior.
Logically; yes, changing the order may make the code work correctly but I wouldn't bet the farm on it for all compilers. Reading and writing of the same variable in a single execution line should never be trusted. The code was written that way because it matches the original question, thus showing that code is wrong.
aaah.... I just tried this on paper and got the thing. I didn't realized Disch and your point as I didn't see what exactly the list is looking like. Although I was saying what you were saying and thought you are saying something else.