I have used such construct in C:
list->head = list->tail = NULL;
and now I consider whether this really mean what I suppose.
<With all the incomplete answers, I have to clarify this a bit.
First, the expression is evaluated right to left:
list->head = (list->tail = NULL);
The standard defines the behaviour of the assignment operator as:
An assignment operator stores a value in the object designated by the left operand. An assignment expression has the value of the left operand after the assignment,111) but is not an lvalue. The type of an assignment expression is the type the left operand would have after lvalue conversion. The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands. The evaluations of the operands are unsequenced.
Problem is now, the standard leaves two ways how to get the value of an assignment.**
// variant 1 (read the left-hand side after writing it)
list->tail = NULL;
list->head = list->tail;
That is, list->tail
is read after it got its value and that value is assigned to list->head
.
// variant 2 (use temporary storage)
typeof(list->tail) temp = NULL; // get the value/type of the RHS of the inner assignment
list->tail = temp;
list->head = temp;
(RHS: right hand side, the term to the right of an operator). Thanks, to @chux, the two assignments could also be swapped.
See footnote 111:
The implementation is permitted to read the object to determine the value but is not required to, even when the object has volatile-qualified type.
The variants behave equivalent with respect of the abstract machine - unless list->tail
is qualified volatile
(more general: any but the leftmost object). Briefly, volatile
tells the compiler the access to an object has side-effects. It is typically used for peripheral hardware registers, e.g. USART. While it is rarely (and if, then often wrongly) used in desktop applications, it is typically used in OS kernel-drivers and bare-metal embedded systems.
Writes and reads to such registers typically pass the data written to the (external) hardware, resp. a read yields the value of such a hardware register. Worse, these registers can be write-only or read-only. For write-only, reading yields indeterminate values which is not related to what was written. Relevant here is that accesses to these objects have to be very carefully controlled and sequenced. There mut be no unexpected read or write.
So, consider the code above. Then the variants will generate different accesses to the hardware:
volatile
lvalues.Note which variant is used is not detrmined by the user, but the compiler. It might even vary for different expressions in the code.
As a consequence, chained assignments are a no-go once volatile
is involved.
it's multiple assignment as your first option is the right one
list->head = list->tail = NULL;
it's a magic of flow.
initially ,tail
is set to NULL
,starting from right to left
list->tail = NULL;
then
list->head = list->tail ;
now tail
is NULL
so the head
will also assigned a NULL
value
Associativity of assignment operator =
is right to left. This means that if there are more than one =
operator in a statement, then the rightmost is evaluated first, then the one left to it, and in that order, untill the leftmost =
operator is evaluated at last.
This means that when you do
list->head = list->tail = NULL;
the right most assignment, i.e. list->tail = NULL
is evaluated first. So list->tail
will be NULL
.
After that list->head = list->tail
will be evaluated. And since list->tail
is NULL
by now (because of previous evaluation- i.e. list->tail = NULL
), now list->head
is also NULL
.
Based on how you are representing in your question, it is like
list->tail = NULL; list->head = list->tail;
Neither of them. It mean
list->tail = NULL;
list->head = list->tail;
1 is incorrect because in case the type of list->tail
is not a pointer, it may yield different result because the value will be converted to the type of the destination of the assignment.
The order of two statements in 2 is incorrect.
The assignment operator is right to left associative.
Thus this expression statement
list->head = list->tail = NULL;
is equivalent to
list->tail = NULL;
list->head = list->tail;
Neither of those is correct.
Since the simple assignment =
operator is right-to-left associative, your expression is identical to:
list->head = (list->tail = NULL);
NULL is assigned to tail, and then tail, which has the value of a null pointer, to head.