原始头指针在 printList 函数中没有改变,但在插入节点时列表确实改变了
Original head pointer not being changed in the printList function, but the list does change when inserting a node
我已经阅读了这两个 posts/answers 关于双 pointers/passing 的使用
When printing out a linked list, why is the original head pointer not changed
Linked list head double pointer passing
但有一件事仍然困扰着我。
printList 函数中的头指针(使用 head = head->next 遍历)在 main 中没有改变,因为即使我们通过引用传递它,该函数也会收到 pointer/address 的副本。
我能理解。
但是为什么在插入像
这样的节点时整个列表会发生变化(更新)
struct node* addLast(struct node* head, struct node* new_node) {
if (head == NULL)
{
head = new_node;
return head;
}
struct node* current = head;
while (current->next != NULL)
{
current = current->next;
}
current->next = new_node;
return head;
}
我们在 main
中调用它
head = addLast(head, node)
我知道该原则适用于 head == NULL 的情况(因为我们 return “新”头),但如果不是,那么我们再次遍历列表并插入节点。
为什么列表会更新(不一定只在这个特定的添加函数中)?那么new_node(由另一个函数用malloc()创建的节点)不也是一个“副本”吗?
how come then the whole list gets changed (updated) when inserting a node?
如您所说,这里有两种情况:
I get that the principle applies to the case when head == NULL (because we return the "new" head)
确实如此。所以你的问题是关于列表不为空并且节点附加到它的其余情况。
在这种情况下,返回值与作为参数给出的 指针 相同:head
不变。
但是,head
指向的节点有自己的next
指针,那个指针可能已经从NULL
变成了指向新节点的指针。因此,虽然 head
没有改变,但 next
指针链将变得 更长 .
想象一下当我们从一个空列表开始时会发生什么,然后使用以下脚本向其中添加节点:
node* createNode(int value) {
node* newNode = malloc(sizeof(node));
newNode->value = value;
newNode->next = NULL;
return newNode;
}
int main() {
node* head = NULL;
head = addLast(head, createNode(1));
head = addLast(head, createNode(2));
head = addLast(head, createNode(3));
// ...
return 0;
}
我刚刚添加了一个函数来创建节点实例:没有惊喜(我希望!)
所以当脚本启动时,我们有 head
:
head: NULL
然后我们调用createNode(1)
其中returns一个指向节点的指针。我们可以用一个方框表示该节点:
┌────────────┐
│ value: 1 │
│ next: NULL │
└────────────┘
指向此节点的指针作为第二个参数传递给 addList
,因此在该函数中我们有:
new_node head: NULL
↓
┌────────────┐
│ value: 1 │
│ next: NULL │
└────────────┘
正如您正确注意到的那样,此节点引用从函数 addList
返回给调用者,调用者将其分配给自己的 head
变量。所以在主程序中我们现在有这个状态:
head
↓
┌────────────┐
│ value: 1 │
│ next: NULL │
└────────────┘
现在到第二个节点:它是用 createNode(2)
创建的,然后用这些参数调用 addList
:
head new_node
↓ ↓
┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │
│ next: NULL │ │ next: NULL │
└────────────┘ └────────────┘
addList
然后创建另一个变量 current
以与 head
:
相同的引用开始
current
head new_node
↓ ↓
┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │
│ next: NULL │ │ next: NULL │
└────────────┘ └────────────┘
while
循环条件不成立,所以不会迭代。然后执行 current->next = new_node
,这是最重要的赋值:它在列表的 last 节点与新节点之间建立 link:
current
head new_node
↓ ↓
┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │
│ next: ──────────> │ next: NULL │
└────────────┘ └────────────┘
最后,head
返回给调用者,调用者将其分配给他们的 head
变量——这实际上是一个虚拟分配,因为 head
没有改变。 did 改变的是 head
指向的 linked 列表的长度。
这应该可以解释了,但是让我们再添加一个节点:create_node(3)
传递给 addList
,所以在 addList
中我们有这个状态:
current
head new_node
↓ ↓
┌────────────┐ ┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │ │ value: 3 │
│ next: ──────────> │ next: NULL │ │ next: NULL │
└────────────┘ └────────────┘ └────────────┘
这次while
条件为真,所以current = current->next
会让我们进入这个状态:
head current new_node
↓ ↓ ↓
┌────────────┐ ┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │ │ value: 3 │
│ next: ──────────> │ next: NULL │ │ next: NULL │
└────────────┘ └────────────┘ └────────────┘
然后 while
循环将退出,并且 current->next = new_node
被执行:
head current new_node
↓ ↓ ↓
┌────────────┐ ┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │ │ value: 3 │
│ next: ──────────> │ next: ──────────> │ next: NULL │
└────────────┘ └────────────┘ └────────────┘
并且 addList
通过返回(未更改的)head
指针终止。主程序再次执行对其自身 head
的赋值,即使该指针没有变化。
我希望这能澄清即使 head
不再改变,next
指针链 确实 改变:尾节点的 next
指针从 NULL
变为新节点的地址。
我已经阅读了这两个 posts/answers 关于双 pointers/passing 的使用
When printing out a linked list, why is the original head pointer not changed
Linked list head double pointer passing
但有一件事仍然困扰着我。
printList 函数中的头指针(使用 head = head->next 遍历)在 main 中没有改变,因为即使我们通过引用传递它,该函数也会收到 pointer/address 的副本。 我能理解。
但是为什么在插入像
这样的节点时整个列表会发生变化(更新)struct node* addLast(struct node* head, struct node* new_node) {
if (head == NULL)
{
head = new_node;
return head;
}
struct node* current = head;
while (current->next != NULL)
{
current = current->next;
}
current->next = new_node;
return head;
}
我们在 main
中调用它head = addLast(head, node)
我知道该原则适用于 head == NULL 的情况(因为我们 return “新”头),但如果不是,那么我们再次遍历列表并插入节点。
为什么列表会更新(不一定只在这个特定的添加函数中)?那么new_node(由另一个函数用malloc()创建的节点)不也是一个“副本”吗?
how come then the whole list gets changed (updated) when inserting a node?
如您所说,这里有两种情况:
I get that the principle applies to the case when head == NULL (because we return the "new" head)
确实如此。所以你的问题是关于列表不为空并且节点附加到它的其余情况。
在这种情况下,返回值与作为参数给出的 指针 相同:head
不变。
但是,head
指向的节点有自己的next
指针,那个指针可能已经从NULL
变成了指向新节点的指针。因此,虽然 head
没有改变,但 next
指针链将变得 更长 .
想象一下当我们从一个空列表开始时会发生什么,然后使用以下脚本向其中添加节点:
node* createNode(int value) {
node* newNode = malloc(sizeof(node));
newNode->value = value;
newNode->next = NULL;
return newNode;
}
int main() {
node* head = NULL;
head = addLast(head, createNode(1));
head = addLast(head, createNode(2));
head = addLast(head, createNode(3));
// ...
return 0;
}
我刚刚添加了一个函数来创建节点实例:没有惊喜(我希望!)
所以当脚本启动时,我们有 head
:
head: NULL
然后我们调用createNode(1)
其中returns一个指向节点的指针。我们可以用一个方框表示该节点:
┌────────────┐
│ value: 1 │
│ next: NULL │
└────────────┘
指向此节点的指针作为第二个参数传递给 addList
,因此在该函数中我们有:
new_node head: NULL
↓
┌────────────┐
│ value: 1 │
│ next: NULL │
└────────────┘
正如您正确注意到的那样,此节点引用从函数 addList
返回给调用者,调用者将其分配给自己的 head
变量。所以在主程序中我们现在有这个状态:
head
↓
┌────────────┐
│ value: 1 │
│ next: NULL │
└────────────┘
现在到第二个节点:它是用 createNode(2)
创建的,然后用这些参数调用 addList
:
head new_node
↓ ↓
┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │
│ next: NULL │ │ next: NULL │
└────────────┘ └────────────┘
addList
然后创建另一个变量 current
以与 head
:
current
head new_node
↓ ↓
┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │
│ next: NULL │ │ next: NULL │
└────────────┘ └────────────┘
while
循环条件不成立,所以不会迭代。然后执行 current->next = new_node
,这是最重要的赋值:它在列表的 last 节点与新节点之间建立 link:
current
head new_node
↓ ↓
┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │
│ next: ──────────> │ next: NULL │
└────────────┘ └────────────┘
最后,head
返回给调用者,调用者将其分配给他们的 head
变量——这实际上是一个虚拟分配,因为 head
没有改变。 did 改变的是 head
指向的 linked 列表的长度。
这应该可以解释了,但是让我们再添加一个节点:create_node(3)
传递给 addList
,所以在 addList
中我们有这个状态:
current
head new_node
↓ ↓
┌────────────┐ ┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │ │ value: 3 │
│ next: ──────────> │ next: NULL │ │ next: NULL │
└────────────┘ └────────────┘ └────────────┘
这次while
条件为真,所以current = current->next
会让我们进入这个状态:
head current new_node
↓ ↓ ↓
┌────────────┐ ┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │ │ value: 3 │
│ next: ──────────> │ next: NULL │ │ next: NULL │
└────────────┘ └────────────┘ └────────────┘
然后 while
循环将退出,并且 current->next = new_node
被执行:
head current new_node
↓ ↓ ↓
┌────────────┐ ┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │ │ value: 3 │
│ next: ──────────> │ next: ──────────> │ next: NULL │
└────────────┘ └────────────┘ └────────────┘
并且 addList
通过返回(未更改的)head
指针终止。主程序再次执行对其自身 head
的赋值,即使该指针没有变化。
我希望这能澄清即使 head
不再改变,next
指针链 确实 改变:尾节点的 next
指针从 NULL
变为新节点的地址。