让我们一步一步分析方法。
首先我们考虑以下简单的程序。
#include <stdio.h>
#include <stdlib.h>
struct TreeNode{
int val;
struct TreeNode *left;
struct TreeNode *right;
};
void create( struct TreeNode *head, int val )
{
head = malloc( sizeof( struct TreeNode ) );
head->val = val;
head->left = NULL;
head->right = NULL;
}
int main(void)
{
struct TreeNode *head = NULL;
printf( "Before calling the function create head == NULL is %s\n",
head == NULL ? "true" : "false" );
create( head, 10 );
printf( "After calling the function create head == NULL is %s\n",
head == NULL ? "true" : "false" );
return 0;
}
程序输出是
Before calling the function create head == NULL is true
After calling the function create head == NULL is true
如您所见,main 中的指针 head 没有改变。原因是该函数处理原始指针head 的值的副本。所以更改副本不会影响原始指针。
如果你把函数参数重命名为head_parm(为了区分原来的指针head和函数参数)那么你可以想象函数定义及其调用方式如下
create( head, 10 );
//...
void create( /*struct TreeNode *head_parm, int val */ )
{
struct TreNode *head_parm = head;
int val = 10;
head_parm = malloc( sizeof( struct TreeNode ) );
//...
即在函数内部创建了一个局部变量head_parm,该变量由参数头的值初始化,并且该函数局部变量head_parm在函数内部发生变化。
表示函数参数是按值传递的。
要更改在 main 中声明的原始指针 head,您需要通过引用传递它。
在 C 中,通过引用传递的机制是通过指向对象的指针间接传递对象来实现的。因此,在函数中取消引用指针,您将获得对原始对象的直接访问。
所以让我们用下面的方式重写上面的程序。
#include <stdio.h>
#include <stdlib.h>
struct TreeNode{
int val;
struct TreeNode *left;
struct TreeNode *right;
};
void create( struct TreeNode **head, int val )
{
*head = malloc( sizeof( struct TreeNode ) );
( *head )->val = val;
( *head )->left = NULL;
( *head )->right = NULL;
}
int main(void)
{
struct TreeNode *head = NULL;
printf( "Before calling the function create head == NULL is %s\n",
head == NULL ? "true" : "false" );
create( &head, 10 );
printf( "After calling the function create head == NULL is %s\n",
head == NULL ? "true" : "false" );
return 0;
}
现在程序输出是
Before calling the function create head == NULL is true
After calling the function create head == NULL is false
在您的问题程序中,您没有像上面的程序中那样声明指向头节点的指针
struct TreeNode *head = NULL;
你动态分配了这个指针。事实上,你在你的程序中所做的事情如下
#include <stdio.h>
#include <stdlib.h>
struct TreeNode{
int val;
struct TreeNode *left;
struct TreeNode *right;
};
void create( struct TreeNode **head, int val )
{
*head = malloc( sizeof( struct TreeNode ) );
( *head )->val = val;
( *head )->left = NULL;
( *head )->right = NULL;
}
int main(void)
{
struct TreeNode **p2r = malloc( sizeof( struct TreeNode * ) );
*p2r = NULL;
printf( "Before calling the function create *p2r == NULL is %s\n",
*p2r == NULL ? "true" : "false" );
create( p2r, 10 );
printf( "After calling the function create *p2r == NULL is %s\n",
*p2r == NULL ? "true" : "false" );
return 0;
}
程序输出是
Before calling the function create *p2r == NULL is true
After calling the function create *p2r == NULL is false
与之前的程序相比,当您使用struct TreeNode ** 类型的表达式&head 调用函数create 时,现在引入了一个中间变量p2r,它存储表达式@987654340 的值@由于这段代码sn-p
struct TreeNode **p2r = malloc( sizeof( struct TreeNode * ) );
*p2r = NULL;
那是你早期调用函数create like
create( &head, 10 );
现在实际上你正在调用函数
struct TreeNode **p2r = &head; // where head was allocated dynamically
create( p2r, 10 );
同样的情况也发生在您的程序中。也就是说,在函数插入取消引用指针 p2r 时,您可以直接访问指向头节点的指针
if (parent_ptr == NULL){
*p2r = new_node;
^^^^
}
因此,函数将指针更改为指向通过指针p2r 引用传递的头节点。
其他节点左右的数据成员也通过使用指针parent_ptr对它们的引用来改变
else if (parent_ptr->val < val){ //then insert on the right
parent_ptr -> right = new_node;
^^^^^^^^^^^^^^^^^^^
}else{
parent_ptr -> left = new_node;
^^^^^^^^^^^^^^^^^^
}