关于双重指针的一个问题,外加一幅漫画犒劳热心鱼油!
本帖最后由 ★远处的灯火 于 2013-11-23 09:40 编辑大家好,我是石头,首先贴一张关于爱情的漫画犒劳一下众鱼油,希望对众鱼油{:7_169:}的爱情观有所启迪
然后是石头关于双重指针的一个疑惑点,希望明白的鱼油热心指点一下,在下不胜感激!
#include <stdio.h>
#include <stdlib.h>
typedef struct _tree
{
int data;
struct _tree * next;
}tree;
void InitTree1(tree *p)
{
p = (tree*) malloc(sizeof(tree));
p->data = 0;
p->next = NULL;
return ;
}
void InitTree2(tree **p)
{
(*p) = (tree*)malloc(sizeof(tree));
(*p)->data = 0;
(*p)->next = NULL;
return ;
}
int main()
{
tree p1, *p2;
InitTree1(&p1);
InitTree2(&p2);
return 0;
}
不好意思,这个问题就像递归一样,我虽然能写出来,但是我总感觉犯晕,也不知道我写的是否有错误,最重要的是我甚至说不出自己的真正问题所在,我勉强把自己的疑问写一部分,请问
1个代码中的p1和p2有什么区别?
2这两种初始化p1和p2的方法有什么区别?
3哪种方法根好一点?
4为什么我见很多时候都是用的第二种方法而少用第一种方法?
5可以讲解一下双重指针的一些常见问题吗?
可能我问的也不是特别好,但是还是希望各位热心的鱼油可以为我解答疑惑!
看看是什么 隐藏内容无任何东西,希望楼主贴出代码 感谢楼主分享,顶贴支持~ 这是要闹哪样。{:1_1:} 楼主,有这个疑惑,是正常的,证明楼主,还是个对C学习比较认真的人,在很久以前,我对二级指针和一级指针,指针数组,数组指针,函数指针,指针函数,等等,满是疑惑!
现回归正题:
楼主,可以用VC调试一下程序,就知道原因了,不过截图太麻烦了,我就直接告诉你原理得了:
正对你的问题: InitTree1()使用方式是错误的,在main主函数调用完毕InitTree1后,你可以看到p1根本没有分配地址,因为在InitTree1函数中,你是给实参的拷贝值分配了内存地址,调用完InitTree1后, p1根本没有分配成功地址。
InitTree2()分配是正确的.
因为InitTree2是给实参p2的地址的内容(二级指针中存储的内容是一级指针的地址),给一级指针分配空间后,虽说这里传递过去的也是p2的值拷贝,但是已经改变了指向的内容的值,就是说p2和传递过去的p2的拷贝,指向的内容都被分配了空间,故InitTree2是正确的。
你之所以看到都是使用InitTree2,那是因为InitTree2是正确的用法。
楼主记住C语言中,传递参数都是值拷贝,如果希望在函数中给指针分配地址空间,要么通过函数返回值,要么通过二级指针。
不知道,说的你是否理解! 看看是什么
看楼主这么认真,我就简单说一下吧
首先这两种形式都是对的,先说第一种:
第一种是传入一个结构体实例的地址,所以这时需要在main函数中实例化一个tree t1,那么InitTree1(&t1),传入的就是对象t1的地址,InitTree1函数中就是对main中的t1进行初始化,这种方式需要传入一个已存在的对象实例,如果你只是定义一个tree* p2,然后InitTree1(p2),显然是有问题的,因为p2是一个野指针或者你定义成一个空指针,那么传进去的p2是一个地址,比如0x0012ffc4或者干脆是0x00000000(NULL),形参p则是仅仅拷贝这个值,而后p又malloc,指向了另一个在InitTree1中实例化的tree,返回之后,p1还是0x0012ffc4或者是NULL,当然有一种方式可以这样写:
tree* InitTree1(tree *p)
{
p = (tree*) malloc(sizeof(tree));
p->data = 0;
p->next = NULL;
return p;
}
int main()
{
tree* p2;
p2 = InitTree1(p2);
return 0;
}
再说第二种,第二种是二重指针,其实也就是指向p2的指针,InitTree2(&p2)(指针变量p2本体的地址),形参复制的是指向p2地址的指针p,这个二级指针p经过解引用*p ,对指向的地址重新赋值为一个malloc的tree,那么main中实参p2所指的tree结构也就是刚刚malloc出来的tree。显然,这种无需返回。
逻辑上比较绕,所以说的也不是太清楚,希望你能理解吧 补充说一点,如果你想在把一个实例化的对象t1传进InitTree1中,那么InitTree1中的malloc就需要注掉,因为一旦p指向了另一个在InitTree1中实例化的对象后,进一步初始化的就是这个子函数对象成员,而不是main中的那个对象成员。
所以对于InitTree1写法,我给出两种形式:
void InitTree1(tree *p)
{
p->data = 0;
p->next = NULL;
return ;
}//tree t1; InitTree(&t1);
tree* InitTree1(tree *p)
{
p = (tree*)malloc(sizeof(tree));
p->data = 2;
p->next = NULL;
return p;
}//tree *p1; p1=InitTree1(p1);
看解答,还是能学到不少东西的! 晕菜了:sleepy: {:5_100:}漫画挺不错 漫画挺不错{:5_103:} 学些了 。。。 嗯,很好看的漫画啊:lol: 看看运气~~~~~~~~~~~~ 漫画点赞,还有 第二种方法调用的函数 可以改变 *p 的值(当然也可以改变 p的值), 第一种方法调用的函数 可以改变 p 的值,你说哪种更好╮(╯▽╰)╭ #include <stdio.h>
#include <stdlib.h>
typedef struct _tree
{
int data;
struct _tree * next;
}tree;
void InitTree1(tree *p) //这个应该没有疑问
{
p =(tree* )malloc(sizeof(tree)); //这里不需要强制类型转换。
//malloc(sizeof(T))返回T*型值,“=”左右两边类型一样,符合语法。
p->data = 0;
p->next = NULL;
return ;
}
void InitTree2(tree **p) //这说过了
{
(*p) = (tree*)malloc(sizeof(tree)); //p内记录的是tree*型变量的地址,对其取*,即*p指的
//就是tree* 变量,即指向tree型变量的指针。一句话(*p)相当于上边的InitTree1中的p。当然
//这里也不需要强制类型转换。
(*p)->data = 0;
(*p)->next = NULL;
return ;
}
int main()
{
tree p1, *p2; //p1为tree型变量,p2为指向tree型变量的指针。
InitTree1(&p1);//&p1是取tree型变量的地址
InitTree2(&p2);//&p2为取指向tree型变量的指针变量的地址,它的
//类型为tree *型 根据 T* = &T语法规定,这里T为tree*型。所以形参我要
//用一个tree** 型的指针来存这个值。
return 0;
}
//综上,两个初始化是等价的。关于二者优劣,因为单边二叉树这里我还没学习到,所以
//抱歉,但在这个程序我建议用第一种,如果程序有添加其他函数,需具体问题具体对待。 这是什么啊??? 我是路过打酱油的,看一看