尽管进行了相应的更正,为啥指向指针的指针与指针的处理方式不同
Posted
技术标签:
【中文标题】尽管进行了相应的更正,为啥指向指针的指针与指针的处理方式不同【英文标题】:Why is a pointer to pointer treated differently than a pointer in spite of corresponding corrections尽管进行了相应的更正,为什么指向指针的指针与指针的处理方式不同 【发布时间】:2021-09-23 09:37:48 【问题描述】:我正在尝试从双向链表中删除一个节点。函数 deleteNode() 将接收链表的头部和要删除的节点。但是,根据我是将要删除的节点作为指针还是指向指针的指针传递,代码的行为会有所不同(尽管在语法上进行了相应的更正)。
这是我的双向链表节点结构 -
struct DNode
int val;
struct DNode* next;
struct DNode* prev;
DNode(int val) //constructor
this->val = val;
next = NULL;
prev = NULL;
;
以下代码运行良好:
void deleteNode(struct DNode** head, struct DNode* deleteMe)
//head is pointer to pointer, deleteMe is pointer
if(!(*head) || !deleteMe)
cout<<"\nDeletion not possible";
return;
if(deleteMe->prev != NULL)
deleteMe->prev->next = deleteMe->next;
if(deleteMe->next != NULL)
deleteMe->next->prev = deleteMe->prev;
if(deleteMe == *head)
*head = deleteMe->next;
delete deleteMe;
对应的驱动代码-
/*at this point, I have pushed elements into the Linked List to make it 4->3->2->1, where 4 is
the head*/
deleteNode(&head, head);
deleteNode(&head, head->next);
deleteNode(&head, head->next);
但是,当我将两个参数作为指向指针的指针发送时,我在运行时遇到了崩溃:
void deleteNode(struct DNode** head, struct DNode** deleteMe)
if(!(*head) || !(*deleteMe))
cout<<"\nDeletion not possible";
return;
if((*deleteMe)->prev != NULL)
(*deleteMe)->prev->next = (*deleteMe)->next;
if((*deleteMe)->next != NULL)
(*deleteMe)->next->prev = (*deleteMe)->prev;
if(*deleteMe == *head)
*head = (*deleteMe)->next;
delete *deleteMe;
对应的驱动代码:
/*at this point, I have pushed elements into the Linked List to make it 4->3->2->1, where 4 is
the head*/
deleteNode(&head, &head);
deleteNode(&head, &head->next);
deleteNode(&head, &head->next);
PS - 这不完全是删除的问题:
如果您在错误代码中注释行 delete *deleteMe,代码将运行但输出会有所不同。
工作代码的输出:3
注释删除行后错误代码的输出:3->1
【问题讨论】:
不要用评论告诉我们head
是什么,而是在代码中显示它。创建minimal reproducible example。
第一种情况,第二个参数是按值传递的。因此(在函数内部)*head
的更改不能与deleteMe
的更改(指向指向的对象)交互。将第二个参数作为指向指针的指针传递意味着(内部)函数*head
和*deleteMe
(或(*deleteMe)->next
或(*deleteMe)->prev
)可能是相同的对象。论点之间相互作用的差异解释了行为的差异。为main()
中的变量和deleteNode()
中的变量或参数赋予不同的名称可能会使交互在视觉上更加明显。
【参考方案1】:
这里,当你通过&head
和&head
,
if(*deleteMe == *head)
*head = (*deleteMe)->next;
*deleteMe
和*head
是同一个对象(你传入的地址的对象),赋值后还是同一个对象。
(即相当于*head = (*head)->next;
和*deleteMe = (*deleteMe)->next
。)
所以这个,
delete *deleteMe;
在这种情况下与delete *head
做同样的事情,然后事情就发展了。
在第一种情况下,*head
和deleteMe
是不同的对象,因此分配给*head
不会影响deleteMe
。
作为说明,这就是发生的情况(“main_head”是main
中的变量)。
当你进入函数时,你有这种情况:
head
|
v +---+ +---+
main_head---->| ---->| ----> ...
^ +---+ +---+
| ^
deleteMe |
(*deleteMe)->next
然后分配*head = (*deleteMe)->next
:
head +-------------+
| | |
| | v
v | +---+ +---+
main_head-+ | ---->| ----> ...
^ +---+ +---+
|
deleteMe
您可以看到head
和deleteMe
仍然指向main
的head
,这就是您为其赋值的对象。
使用工作代码,您可以从以下内容开始:
head
|
v +---+ +---+
main_head---->| ---->| ----> ...
+---+ +---+
^ ^
| |
deleteMe deleteMe->next
最终得到
head +-------------+
| | |
| | v
v | +---+ +---+
main_head-+ | ---->| ----> ...
+---+ +---+
^ ^
| |
deleteMe deleteMe->next
【讨论】:
等一下。当您说“分配后它们仍然是同一个对象”时,这怎么可能?我看到它的方式是,当我传递 &head 和 &head 时,(在函数中)*head 和 *delete 是开头同一节点的别名。当我说*head = (*deleteMe)->next;
时, *head 现在指向链表中的第二个节点,而 *deleteMe 仍然指向第一个节点。所以delete *deleteMe;
不可能和delete *head;
意思一样
是的,*head
和 *deleteMe
是同一个对象的“别名”。因此,当您分配给*head
时,这会影响*deleteMe
,因为它是同一件事。我将添加一点 ASCII 艺术来说明。
兄弟,ASCII 艺术非常有帮助,谢谢我现在明白了以上是关于尽管进行了相应的更正,为啥指向指针的指针与指针的处理方式不同的主要内容,如果未能解决你的问题,请参考以下文章