Мой вопрос: почему new_node->next
назначено разыменованной head
?
Поскольку параметр head
функции push
является указателем на указатель головы. Вы должны разыменовать его один раз, чтобы получить (предполагаемый) указатель на головной узел вашего связанного списка. Это, конечно, должно совпадать на стороне вызывающей стороны - они должны передавать адрес указателя головы списка.
Я думал, что head
должен быть указателем, что делает *head
следующий node_t
сам по себе.
Представленный код немного запутывает это, используя один и тот же идентификатор "head
" в разных областях для представления разных уровней косвенности. В функции push
параметр head
равен node_t **
. Да, это указатель, но не на головной узел. Вместо этого он указывает на другой указатель, который, в свою очередь, указывает на головной узел. *head
есть node_t *
, а не node_t
.
Во-вторых, последняя строка *head = new_node
совпадает с head = &new_node
? Более того, это то же самое, что и **head = &new_node
?
Нет, все они разные, что сразу следует из того факта, что head
, *head
и **head
все обозначают разные предметы. В функции push()
,
head
обозначает параметр функции, который имеет тип node_t **
и является локальным для функции . Назначение head = &new_node
будет корректным по типу и в целом допустимым, установив head
для указания на локальную переменную функции new_node
, но это будет бесполезно, поскольку оно ничего не передает обратно вызывающей функции и служит в самой функции нет особой цели.
*head
, конечно, обозначает объект, на который указывает head
, как выбрано вызывающим функцией. Он имеет тип node_t *
. Этот объект указателя доступен вызывающей стороне и может, например, быть одной из локальных переменных вызывающей стороны. Присвоение *head = new_node
изменяет этот объект, доступный для вызывающей стороны, и, следовательно, его эффект виден вызывающей стороне, для чего параметр является двойным указателем.
**head
обозначает объект, на который указывает *head
, node_t
. Этот объект также доступен вызывающей стороне, но его изменение не будет служить целям функции. В этом случае вызывающая сторона останется с тем же объектом node_t
, но с другим содержимым. В любом случае выражение &new_node
, a node_t **
, не имеет правильного типа для присвоения **head
. Если бы кто-то на самом деле хотел назначить ему, то вариант с корректным типом (но семантически неуместным) был бы **head = *new_node
.
Последний вариант был бы наиболее разумным , поскольку мы передаем указатель указателя в качестве аргумента push
.
Причина передачи указателя на указатель заключается в том, что указатель на указатель может быть изменено функцией. В результате указатель вызывающей стороны указывает на узел, выделенный push
, тогда как указатель next
этого узла указывает на узел, на который изначально указывал указатель вызывающей стороны. Это именно то, что нужно для добавления узла в список. Вид вызывающего выглядит примерно так:
до пу sh ()
X --next--> ... --next--> NULL
|
v
Value1 ...
после пу sh (значение2)
Y --next--> X --next--> ... --next--> NULL
| |
v v
Value2 Value1 ...
Если вместо push
вместо присвоено **head
, то результатом будет
X --next--> (depends)
|
v
Value2
Обратите внимание, что Value1
теперь нет, его заменили на Value2
.