V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Tony042
V2EX  ›  C

关于 C++ 函数传入指针的问题(小白求教)

  •  
  •   Tony042 · 2019-07-04 11:22:11 +08:00 · 3616 次点击
    这是一个创建于 1975 天前的主题,其中的信息可能已经有所发展或是发生改变。
    struct PolyNode
    {
        int coef;
        int expon;
        PolyNode *Link;
    };
    
    typedef PolyNode *Polynomial;
    
    struct PolyQueue
    {
        Polynominal Front;
        Polynominal Rear;
    };
    
    typedef PolyQueue *PolyQueuePtrl;
    
    void AddPolyNode(PolyQueuePtrl Ptrl, const int coef, const int expon)
    {
        Polynominal Temp = new PolyNode;
        Temp->coef = coef;
        Temp->expon = expon;
        Temp->Link = nullptr;
        if (Ptrl->Front == nullptr)
            Ptrl->Front = Temp;
        if (Ptrl->Rear != nullptr)
            Ptrl->Rear->Link = Temp;
        Ptrl->Rear = Temp;
    }
    
    void Attach(Polynomial &rear, const int coef, const int expon)
    {
        rear->Link = new PolyNode;
        rear = rear->Link;
        rear->coef = coef;
        rear->expon = expon;
        rear->Link = nullptr;
    }
    

    最近在学习数据结构,在用 C++实现一个队列的时候,有了一个疑问希望能获得各位大佬的解答。我写了两个函数来实现在队列末尾新增一个元素的操作。一个传入的是队列的指针,另外一个传入的是队列里面的 Rear 指针,定义和具体实现在上方代码中,在传入队列指针的时候直接传入即可实现插入的操作,但是在传入 Rear 指针的时候一定要传入 Rear 指针的引用才可以,同样是指针为什么一个传值就可以了,另一个要传引用呢?

    34 条回复    2019-07-04 21:50:29 +08:00
    zjsxwc
        1
    zjsxwc  
       2019-07-04 11:55:14 +08:00
    传指针与传引用并没有问题,

    如图:
    https://i.loli.net/2019/07/04/5d1d78494694f93937.png

    你的代码:
    https://paste.ubuntu.com/p/tD66H7V74M/
    srt180
        2
    srt180  
       2019-07-04 11:57:37 +08:00 via iPhone
    需要改变入参就引用
    jmc891205
        3
    jmc891205  
       2019-07-04 12:03:25 +08:00
    我猜你是插入之后看 ptrl->rear 来检查有没有插入成功
    你的 Attach 只传指针进去的时候 插入其实是成功了的 但是 ptrl->rear 没有更新 所以你以为是插入没有成功
    Tony042
        4
    Tony042  
    OP
       2019-07-04 12:27:04 +08:00 via iPhone
    @zjsxwc 是的,谢谢你,代码是正确的代码,我的意思是,为什么把 Attach 函数第一个参数由&rear 改成 rear 就不对了呢
    zjsxwc
        5
    zjsxwc  
       2019-07-04 12:31:19 +08:00
    @Tony042

    没有不对啊,我发的代码里 函数 AttachTest 的结果是对的啊
    Tony042
        6
    Tony042  
    OP
       2019-07-04 12:31:19 +08:00 via iPhone
    @srt180 但是两个都是指针呀,我目前的理解是传入指针指向的结构体里的指针与传值一样,如需修改得声明饮用,但是传指针的话是不用声明引用就可以直接修改的。
    Tony042
        7
    Tony042  
    OP
       2019-07-04 12:33:40 +08:00
    @zjsxwc 我贴的代码是正确的代码,但是如果把 void Attach(Polynomial &rear, const int coef, const int expon)这个声明改成 void Attach(Polynomial rear, const int coef, const int expon) 这个函数就不生效了,所以我是想问 rear 和&rear 的区别
    Tony042
        8
    Tony042  
    OP
       2019-07-04 12:36:10 +08:00
    @jmc891205 对的是这样的,可为什么没更新呢,我通过 attch 函数传入的那个参数值就是 Ptrl->Rear, 比如 Attach ( Ptrl->Rear,3,5)
    zjsxwc
        9
    zjsxwc  
       2019-07-04 12:40:15 +08:00
    @Tony042
    我发的代码里 &rear 改成 rear 后, 仍旧生效。

    rear 和&rear 的区别,
    简单来说 只是 “副作用”的区别,
    &引用方式是有副作用的,在子函数里对变量的修改,会影响到外面调用函数里的变量值,
    一般&引用方式传值是为了能够返回变量,
    比较 C++不能和 golang 那样一次性 return 多个值。
    zjsxwc
        10
    zjsxwc  
       2019-07-04 12:41:11 +08:00
    “比较 ” ---》 “毕竟”
    Tony042
        11
    Tony042  
    OP
       2019-07-04 12:44:13 +08:00
    @zjsxwc xi 谢谢您的回复,我运行了您的,如果输出 pqp->rear->coef 的值的话,AttachTest 是不生效的,我在您的代码后面加了一个 cout 输出,请您瞅一下 https://paste.ubuntu.com/p/pzTNnPC74x/
    srt180
        12
    srt180  
       2019-07-04 12:46:54 +08:00 via iPhone
    @Tony042 指针就是地址,加不加引用代码逻辑都完成了。不同的是加引用的话 attach 函数 rear 的实参变成了入参的 link,不加引用的话 rear 的实参还是入参,相当于对参数而言加了引用就是读写权限,不加就是只读。
    AddPolynode 函数不需要引用是因为入参只读了。
    zjsxwc
        13
    zjsxwc  
       2019-07-04 12:49:20 +08:00
    @Tony042

    理解了你所谓 “ AttachTest 是不生效”,
    很简单,
    因为 AttachTest 没有副作用,所以外面的 pqp->Rear 没有更新
    Tony042
        14
    Tony042  
    OP
       2019-07-04 12:49:53 +08:00
    @srt180 谢谢讲解,感觉有点转过来了,AddPolynode 和 Attach 的区别还是在于指针更改了没有,由于 Attach 更改了指针所以要用引用,刚学 C++没多久,遇到指针还是很迷,还好有你们的帮助
    Tony042
        15
    Tony042  
    OP
       2019-07-04 12:51:34 +08:00
    @zjsxwc 对的,没错,实际上值已经加上了,现在理解了
    Tony042
        16
    Tony042  
    OP
       2019-07-04 12:52:24 +08:00
    @jmc891205 对的,我现在理解了,值已经加在链表上面了,但是 rear 没有更新,谢谢您
    Tony042
        17
    Tony042  
    OP
       2019-07-04 12:53:54 +08:00
    @zjsxwc 对了,能麻烦告诉下你用的 IDE 是什么么,看着很不错哇
    zjsxwc
        18
    zjsxwc  
       2019-07-04 12:56:59 +08:00   ❤️ 1
    @Tony042

    clion
    lvdong
        19
    lvdong  
       2019-07-04 13:00:58 +08:00   ❤️ 1
    指针本身也是变量,如果要改变指针自身的值,就需要引用,仅改变指针指向变量的值,指针按照值传递就可以了
    stackexplode
        20
    stackexplode  
       2019-07-04 14:26:36 +08:00
    越搞不懂 C++,在学习过程中就越不要用那么多 typedef,什么 Polynomial &,真的非常容易混淆
    你参数展开成 PolyNode *& rear,一下就懂了,传进来的是指针的引用,你一旦修改 rear,传进来的指针值就会被修改
    如果是 PolyNode * rear,传进来只是一个指针,也就是一个值,修改 rear 就不会对传入方有影响
    b00tyhunt3r
        21
    b00tyhunt3r  
       2019-07-04 16:32:51 +08:00
    nullptr 是什么?宏吗?
    b00tyhunt3r
        22
    b00tyhunt3r  
       2019-07-04 16:37:52 +08:00
    @b00tyhunt3r 饿 查了一下原来是 11 的新类型,请问这里为什么不直接用 NULL ?
    jmc891205
        23
    jmc891205  
       2019-07-04 16:47:17 +08:00
    @b00tyhunt3r
    假设你有一个函数 void test_null(int ptr)

    test_null(nullptr)会在编译的时候就报错
    test_null(NULL)不会
    b00tyhunt3r
        24
    b00tyhunt3r  
       2019-07-04 17:00:21 +08:00
    @jmc891205 这个理解,NULL 相当于还是有个 0 值
    但楼主这里用 nullptr 的意义是?
    jmc891205
        25
    jmc891205  
       2019-07-04 17:08:50 +08:00
    @b00tyhunt3r
    意义是养成在所有地方都用 nullptr 抛弃 NULL 的好习惯
    b00tyhunt3r
        26
    b00tyhunt3r  
       2019-07-04 18:17:07 +08:00
    LZ,你的代码我原样复制在 mac+gcc4.2.1 上跑了一遍,
    结果很奇怪,直接报了错 segmentation fault:11,

    排查了一下发现是这句的问题:
    Ptrl->Rear->Link = Temp;

    具体来说把 “->Link ”注释掉就好了,应该是直接调用了 link 这个空指针。
    你运行没问题吗?
    也请大佬帮忙看一下谢谢
    @jmc891205
    @stackexplode
    @zjsxwc
    @srt180
    jmc891205
        27
    jmc891205  
       2019-07-04 19:07:09 +08:00 via iPhone
    @b00tyhunt3r 把你调用 lz 函数的代码贴出来
    b00tyhunt3r
        28
    b00tyhunt3r  
       2019-07-04 19:30:59 +08:00
    #include <iostream>
    using namespace std;

    struct poly
    {
    int coef;
    int expon;
    poly* link;
    };
    typedef poly* node;

    struct polyqueue
    {
    node front;
    node rear;
    };
    typedef polyqueue* polyq;

    void addpoly(polyq q, const int coef, const int expon)
    {
    node tmp = new poly;
    tmp->coef = coef;
    tmp->expon = expon;
    tmp->link = nullptr;
    if(q->front == nullptr)
    {q->front = tmp;}
    if(q->rear != nullptr)
    {q->rear->link = tmp;}
    q->rear = tmp;
    }

    int main()
    {
    polyq x=new polyqueue;
    addpoly(x,1,1);

    return 0;
    }


    @jmc891205 是一样的啊
    jmc891205
        29
    jmc891205  
       2019-07-04 20:22:28 +08:00
    @b00tyhunt3r
    类成员是指针的话 编译器自动生成的默认构造函数 不保证能正确地初始化他们为 nullptr
    Tony042
        30
    Tony042  
    OP
       2019-07-04 20:47:22 +08:00
    @jmc891205 那为什么这个代码 https://paste.ubuntu.com/p/tD66H7V74M/运行没问题,但 @b00tyhunt3r 的就有问题呢,我在 windows 下用 clang 跑了一遍,遇到的也是相同的情况
    Tony042
        31
    Tony042  
    OP
       2019-07-04 20:50:06 +08:00
    @b00tyhunt3r 楼里面的大佬说的应该是对的,我在初始化 PolyQueue 类型指针的时候,是调用了另一个函数显示初始化它们为 nullptr,但是我现在也不太明白为什么 https://paste.ubuntu.com/p/tD66H7V74M/这个代码运行起来没问题,而运行你的代码就会 stackfault
    b00tyhunt3r
        32
    b00tyhunt3r  
       2019-07-04 21:40:45 +08:00
    @Tony042 你的代码我看了, 和我的除了变量名不一样 其实是一模一样啊。
    @jmc891205 的意思是说有一定几率不会成功将两个 link ( front 和 rear 的)初始化为 null 吗?

    也就是说这么写是不安全的?那应该怎样改呢
    谢谢回复!
    jmc891205
        33
    jmc891205  
       2019-07-04 21:46:53 +08:00
    @Tony042
    没有初始化的指针可能是任何值,这个跟编译器的实现、运行程序的环境等等都有关系

    @b00tyhunt3r
    应该在 struct/class 的构造函数中显示地初始化每一个成员变量
    jmc891205
        34
    jmc891205  
       2019-07-04 21:50:29 +08:00
    @b00tyhunt3r
    上面我的回复写了个错别字
    应该在 struct/class 的构造函数中「显式」地初始化每一个成员变量
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2896 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 02:55 · PVG 10:55 · LAX 18:55 · JFK 21:55
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.