鱼C论坛

 找回密码
 立即注册
查看: 13116|回复: 145

关于双重指针的一个问题,外加一幅漫画犒劳热心鱼油!

  [复制链接]
发表于 2013-11-22 10:28:08 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 ★远处的灯火 于 2013-11-23 09:40 编辑

大家好,我是石头,首先贴一张关于爱情的漫画犒劳一下众鱼油,希望对众鱼油{:7_169:}的爱情观有所启迪 沙子.gif

然后是石头关于双重指针的一个疑惑点,希望明白的鱼油热心指点一下,在下不胜感激!

#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可以讲解一下双重指针的一些常见问题吗?
可能我问的也不是特别好,但是还是希望各位热心的鱼油可以为我解答疑惑!




想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2013-11-22 11:11:21 | 显示全部楼层

回帖奖励 +2 鱼币

看看是什么
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-11-22 11:12:46 | 显示全部楼层
隐藏内容无任何东西,希望楼主贴出代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-11-22 11:28:46 | 显示全部楼层
感谢楼主分享,顶贴支持~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-11-22 15:15:28 | 显示全部楼层

回帖奖励 +2 鱼币

这是要闹哪样。{:1_1:}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-11-22 17:04:02 | 显示全部楼层
楼主,有这个疑惑,是正常的,证明楼主,还是个对C学习比较认真的人,在很久以前,我对二级指针和一级指针,指针数组,数组指针,函数指针,指针函数,等等,满是疑惑!
现回归正题:
楼主,可以用VC调试一下程序,就知道原因了,不过截图太麻烦了,我就直接告诉你原理得了:
正对你的问题: InitTree1()使用方式是错误的,在main主函数调用完毕InitTree1后,你可以看到p1根本没有分配地址,因为在InitTree1函数中,你是给实参的拷贝值分配了内存地址,调用完InitTree1后, p1根本没有分配成功地址。
InitTree2()分配是正确的.
因为InitTree2是给实参p2的地址的内容(二级指针中存储的内容是一级指针的地址),给一级指针分配空间后,虽说这里传递过去的也是p2的值拷贝,但是已经改变了指向的内容的值,就是说p2和传递过去的p2的拷贝,指向的内容都被分配了空间,故InitTree2是正确的。

你之所以看到都是使用InitTree2,那是因为InitTree2是正确的用法。
楼主记住C语言中,传递参数都是值拷贝,如果希望在函数中给指针分配地址空间,要么通过函数返回值,要么通过二级指针。
不知道,说的你是否理解!

评分

参与人数 1鱼币 +1 贡献 +1 收起 理由
寂寞的行者4455 + 1 + 1 热爱鱼C^_^

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-11-22 19:32:38 | 显示全部楼层

回帖奖励 +2 鱼币

看看是什么
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-11-23 10:33:15 | 显示全部楼层

回帖奖励 +2 鱼币

看楼主这么认真,我就简单说一下吧
首先这两种形式都是对的,先说第一种:
      第一种是传入一个结构体实例的地址,所以这时需要在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。显然,这种无需返回。

逻辑上比较绕,所以说的也不是太清楚,希望你能理解吧

评分

参与人数 1鱼币 +3 收起 理由
musilintan + 3 恍然大悟啊。。。。感谢楼主啊!!!

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-11-23 10:47:50 | 显示全部楼层
补充说一点,如果你想在把一个实例化的对象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);


想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-11-23 11:21:00 | 显示全部楼层

回帖奖励 +2 鱼币

看解答,还是能学到不少东西的!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-11-23 12:32:50 | 显示全部楼层
晕菜了  :sleepy:
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-12-12 02:32:23 | 显示全部楼层
漫画挺不错
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-12-12 04:43:06 | 显示全部楼层
漫画挺不错
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-12-12 08:53:55 | 显示全部楼层
学些了 。。。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-12-13 14:44:16 | 显示全部楼层
嗯,很好看的漫画啊:lol:
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-5-13 16:07:46 | 显示全部楼层

回帖奖励 +2 鱼币

看看运气~~~~~~~~~~~~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-5-13 18:45:59 | 显示全部楼层
漫画点赞,还有 第二种方法调用的函数 可以改变 *p 的值(当然也可以改变 p的值), 第一种方法调用的函数 可以改变 p 的值,你说哪种更好╮(╯▽╰)╭
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-5-13 22:14:51 | 显示全部楼层
#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;
}
//综上,两个初始化是等价的。关于二者优劣,因为单边二叉树这里我还没学习到,所以
//抱歉,但在这个程序我建议用第一种,如果程序有添加其他函数,需具体问题具体对待。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-5-14 17:29:46 | 显示全部楼层

回帖奖励 +2 鱼币

这是什么啊???
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-5-15 10:40:17 | 显示全部楼层
我是路过打酱油的,看一看
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-3-29 01:37

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表