printList函数中原始头指针没有改变,但插入节点时列表确实发生了变化
Posted
技术标签:
【中文标题】printList函数中原始头指针没有改变,但插入节点时列表确实发生了变化【英文标题】:Original head pointer not being changed in the printList function, but the list does change when inserting a node 【发布时间】:2022-01-07 14:07:51 【问题描述】:我已经阅读了这两个关于使用双指针/通过引用传递的帖子/答案
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 中没有改变,因为即使我们通过引用传递它,函数也会接收指针/地址的副本。 我能理解。
但是当插入一个像这样的节点时,整个列表怎么会改变(更新)
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 的情况(因为我们返回“新”头),但如果不是,则我们再次遍历列表并插入节点。
那么列表是如何更新的(不一定只在这个特定的添加函数中)?那么new_node(另一个函数用malloc()创建的节点)不也是一个“副本”吗?
【问题讨论】:
【参考方案1】:为什么插入节点时整个列表会发生变化(更新)?
正如你所说的,这里有两种情况:
我知道该原则适用于 head == NULL 的情况(因为我们返回“新”头)
的确如此。所以你的问题是关于列表不为空并且附加一个节点的剩余情况。
在这种情况下,返回的值与作为参数给出的 指针 相同: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)
,它返回一个指向节点的指针。我们可以用一个框来表示那个节点:
┌────────────┐
│ 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节点与新节点之间建立链接:
current
head new_node
↓ ↓
┌────────────┐ ┌────────────┐
│ value: 1 │ │ value: 2 │
│ next: ──────────> │ next: NULL │
└────────────┘ └────────────┘
最后,head
被返回给调用者,调用者将其分配给他们的head
变量——这实际上是一个虚拟分配,因为head
没有改变。 改变的是head
指向的链表长度。
这应该可以解释了,但让我们再添加一个节点: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
更改为地址新节点。
【讨论】:
感谢您提供如此直观且非常详细的解释!我明白,这就是为什么我对 printList 函数感到困惑;因为在该函数中,在遍历时,我们确实将 head->next 分配给了我们的头指针(head = head->next)。在这种情况下,头指针是否保持不变,因为我们通常不使用返回值(在大多数情况下我遇到它是一个 void 函数),或者 printList 函数中指向头指针的指针,我们宁愿只是“通过”是吗? 它保持不变,因为head
是一个局部变量,分配给它不会修改main
中另一个同名变量,它的值是作为参数传递的。
啊,我明白了。现在很清楚了,我再次感谢您花时间和详细的澄清。以上是关于printList函数中原始头指针没有改变,但插入节点时列表确实发生了变化的主要内容,如果未能解决你的问题,请参考以下文章