首先看如下代码:
void creatBtree(Node* head) //按前序生成树。
{
head=new Node();
Node* p=head;
int t;
scanf("%d",&t);
if(t==-1)return;
else p->data=t;
creatBtree(p->left);
creatBtree(p->right);
printf("%在函数里,head的地址为%d\n",head);
return;
}
int main()
{
Head *head;
freopen("tree.txt","r",stdin);
printf("在main中,creat前,head的地址为%d\n",head);
creatBtree(head);
printf("在main中,creat后,head的地址为%d\n",head);
freopen("CON", "r", stdin);
/*以上内容生成了一棵树。*/
return 0;
}
输出结果为:
在main中,creat前,head的地址为2130567168
在函数里,head的地址为3754792
在函数里,head的地址为3754864
在函数里,head的地址为3754768
在函数里,head的地址为3754744
在函数里,head的地址为3754984
在函数里,head的地址为3755056
在函数里,head的地址为3754960
在函数里,head的地址为3739392
在main中,creat后,head的地址为2130567168
可以看出,尽管是传地址,但是函数里对head的修改,返回到main后就消失了。
你是传了head这个“结构体的地址”,可你要修改的就是head这个指针本身,你传它进函数有什么用?你要修改它,你就该把它取地址传入函数!(即方案二和方案三)
想修改谁,就要把谁的地址传进去!!!可以用指针(多重指针),也可以传引用!!!
这是因为,这里虽然传的指针,但是是复制指针的值传进去的。
所谓传地址,指的是你传“你要修改的变量”的地址,然后在函数里你可以通过这个地址来修改这个变量。这个临时地址变量依然是通过复制传到函数里面的,任何函数传参都是复制,只看你复制的是什么东西,什么“传指针”“传引用”,本质上都是“获得指针和引用,然后把它们复制给函数”!
在函数里,new 函数给head重新分配了一块内存地址(这里是对指针进行了修改,修改的是那个副本。因此出去之后,这种修改就没了。),也就是说,head重新指向了另一块地址。之后在所有操作都是在这个新地址里进行的。而传进去的那个地址相当于压根儿没用,没有得到任何修改。因此在main里那个head没有被修改。
传指针,只能修改指针指向的内容。不能修改指针(改了也白改,因为传的这个指针,与传值是一样的,是一个副本)。要想修改指针,就要用指向指针的指针。
咋办呢?
方案一:
最常用的方法,在函数里面申请的内存,要return,才能继续使用。
Node* creatBtree() //按前序生成树。
{
int t;
scanf("%d",&t);
if(t==-1)
{
return NULL;
}
Node* head;
head=new Node();
head->data=t;
head->left=creatBtree();
head->right=creatBtree();
printf("%在函数里,head的地址为%d\n",head);
return head;
}
void preOrder(Node* head) //前序遍历
{
if(head==NULL) return;
printf("%d ",head->data);
preOrder(head->left);
preOrder(head->right);
}
方案二:
使用指向指针的指针。
void creatBtree(Node** head) //按前序生成树。 传指针,只能修改指针指向的内容,不能修改指针(改了也白改,因为传的这个指针,与传值是一样的,是一个副本)。要想修改指针,就要用指向指针的指针。
{
int t;
scanf("%d",&t);
*head=new Node();
if(t==-1)
{
*head=NULL;
return;
}
(*head)->data=t;
creatBtree(&(*head)->left);
creatBtree(&(*head)->right);
return;
}
void preOrder(Node *head) //前序遍历
{
Node* p=head;
if(p==NULL) return;
printf("%d ",p->data);
preOrder(p->left);
preOrder(p->right);
}
int main()
{
Head** head;
freopen("tree.txt","r",stdin);
printf("在main中,creat前,head的地址为%d\n",*head);
creatBtree(head);
printf("在main中,creat后,head的地址为%d\n",*head);
freopen("CON", "r", stdin);
/*以上内容生成了一棵树。*/
preOrder(*head);
return 0;
}
方案三:采用传引用的方式。
首先明确一下传引用。
传引用就相当于扩大了这个变量的作用域。
在main里声明了一个变量,通过传引用进入这个函数后,在这个函数里使用这个变量,可以完全与在main里使用这个函数一样。
传引用也是传了个地址进去,但区别在于,在函数里用这个参数时,不需加“*”,直接就能访问地址指向的值。
例如 :
void func(int* t)
{
(*t)++;
}
int main()
{
int *t;
*t=0;
func(t);
printf("%d",*t);
return 0;
}
void func(int &t)
{
t++;
}
int main()
{
int t;
t=0;
func(t);
printf("%d",t);
return 0;
}
以上两个函数运行结果一样,都是1。
从中可以看出,在传&t的函数里,直接修改t,就能修改t的值。
而在传*t的函数里,要想修改t的值,必须使用(*t)取出t所指向的地址里的值。
&t里的t,相当于*t里的(*t)。
因此,第三种方案如下:推荐这一种!!传引用好处多多。
void creatBtree(Node* &head) //按前序生成树。传入head这个指针的引用。(相当于前面的传入了一个指向指针的指针)
{
head=new Node(); //使用head这个指针,就像在main里一样。直接用。
int t;
scanf("%d",&t);
if(t==-1){head=NULL;return;}
else head->data=t;
creatBtree(head->left);
creatBtree(head->right);
printf("%在函数里,head的地址为%d\n",head);
return;
}
void preOrder(Node* head) //前序遍历
{
if(head==NULL) return;
printf("%d ",head->data);
preOrder(head->left);
preOrder(head->right);
}
int main()
{
Head *head; //head是个指向结构体的指针
freopen("tree.txt","r",stdin);
printf("在main中,creat前,head的地址为%d\n",head);
creatBtree(head);
printf("在main中,creat后,head的地址为%d\n",head);
freopen("CON", "r", stdin);
/*以上内容生成了一棵树。*/
preOrder(head);
return 0;
}
实质其实类似于二重指针。&相当于隐式的使用了“*”。
传指针和传引用的区别:
传指针,传进来的是指针的副本(与传值一样);
传引用,不产生副本。
因此
传进来的指针p,可以修改这个指针,比如p=new Node(),让它指向别的地址(尽管这种修改往往只是局部的修改,即修改的是副本,没什么用,不会对原来那个指针的值(即它所指向的地址)产生影响)。
而传进来的引用t,不能对它修改(因为没有产生副本)。不能通过给&t=new Node() 重新赋值让它指向别的地址(不能进行上面那种无谓的修改,也算是好事)。