c++ 传参传值地址_传参是不会传参的

本文通过一道使用链表实现队列的题目,探讨C++中函数传参的三种方式:值传递、指针传递和引用传递。在实现过程中遇到的错误及解决方案,揭示了传参细节的重要性,并强调了深入理解C++基础知识的必要性。
摘要由CSDN通过智能技术生成

故事的起源是一道EOJ

1004. 队列的实现 - 数据结构与算法专题题库 - ECNU Online Judge​acm.ecnu.edu.cn

当时数据结构学的不好,然后就直接过掉了(所以偷过的懒早晚都是要补回来的)

这题要求用链表实现队列,不带调STL库的啊不然算耍赖!(要是非要耍赖也能AC)

思路是简单的,按照题目要求全局设置两个链表头指针,因为是队列,所以先进先出,插入元素就在末端插入,删除元素就让头指针后移一位,但是由于荒废太久这么简单的东西也还是写了好久,更不用说无数的bug让程序无数次卡死,写这篇文章主要是提醒自己一个最为有趣的bug。

去年学链表的时候因为没有概念,基本上都是照着书敲得代码,这次选择先闭着眼睛靠记忆生敲,果然眼高手低各种问题,先来个结构体——

typedef struct node
{
    int data;
    struct node* link;
}NODE;

这道题因为要用到size,我们可以将头指针利用起来,将其存到头指针的data中,所以链表长这样:

39f68a4c4c790af072359f15ed965d13.png

其他的函数定义基本没问题,但是我当时出了个问题,在链表赋值的时候我调用了前面的push方法,但是一开始我就把head值赋了过去,就导致我最终得到的head其实是2倍的head,因为push方法中每次入队会把head->data+1,算个小插曲,直接置零就好。

问题出在pop方法。

我的想法是让第一个有效元素的data直接改成head的data-1,然后让它直接做head:

8c57ad431ea1267e4ecd39200dd31ca1.png
void pop(NODE *head)
{
    if(head->data==0)
    {
        cout<<"ERRORn";
        return;
    }
    else
    {
        (head->link->data)++;
        head=head->link;
    }
}

结果就是程序卡死,我一直没想明白为什么,所以我想了想换了个办法就是让头指针data自减然后直接跳过第一个元素,指针指向第二个:

42bd65a3d2739853e150f2b211986b28.png
void pop(NODE *head)
{
    if(head->data==0)
    {
        cout<<"ERRORn";
        return;
    }
    else
    {
        head->link=(head->link)->link;
        (head->data)--;
        /*
        (head->link->data)++;
        head=head->link;
        */
    }
}

这次居然跑通了,想不明白,然后就去找学长了,此处省略......那么先简单回顾C++传参那些事儿:

函数传参三种方式:分别是值传递、指针传递和引用传递。

一、传值

在函数定义括号中的参数是形参,是给函数内专用的局部变量,等同于函数接收到的是实参的副本,形参的值在函数内部如果被改变,对实参没有影响

二、传指针

形参为指向实参地址的指针,当对形参操作时,等同于直接通过地址操作实参

三、传引用

下面这段引自大佬博客,说的很清楚,链接走这里:

浅谈C++三种传参方式 - scyq - 博客园​www.cnblogs.com
1. 指针从本质上是一个变量,是一个整形变量,存放的是另一个变量的地址。指针在逻辑上是独立的,它可以被改变,甚至能改变它的值(指向其他地址),并且可以取出对应内存中的数据。
2. 引用可以理解为外号,是另一个变量的同义词,它在逻辑上具有 依附性,所以C++也规定引用的在创立的时候就必须被初始化(现有一个变量,然后创建对该变量的引用)。 而且其引用的对象在其整个生命周期中不能被改变,即自始至终只能依附于同一个变量(初始化的时候代表的是谁的别名,就一直是谁的别名,不能变)。
3. 在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。

64aef8ed4b27ac256890b339f20ae440.png
盗学长图

7518092d5047edf06a3f241f322fe148.png

也就是说函数调用前编译器自动创建新的变量,将其赋值成传入的参数,我之前那种传指针的写法,在pop函数里面它实际上是创建了一个新的指针,也指向传进来的head 的队首元素,而我后来对它进行修改改变的只是函数内部的head指向,对全局head没有影响;之所以后来的方法管用是因为全局head和新建head中存着的link,也即队首地址是一样的,也就是它们指向的这个地址是一样的,所以对这个地址操作,让它指向别处,不论是main中的head还是函数里的head都是完全没有问题的。

新旧变量指向的link是相同的,而本身是不同的;所以我让link的这块地址指向别处,对哪个head操作都OK,但是新旧head本身的地址是不一样的,改它莫得用。

最后AC代码,请路过大佬不吝赐教:

#include<bits/stdc++.h>
using namespace std;

typedef struct node
{
    int data;
    struct node* link;
}NODE;

int Q,x;
string obj,op;
NODE *a,*b;

void pop(NODE *head)
{
    if(head->data==0)
    {
        cout<<"ERRORn";
        return;
    }
    else
    {
        head->link=(head->link)->link;
        (head->data)--;
    }
}

void push(NODE *head,int x)
{
    NODE *q=(NODE *)malloc(sizeof(NODE));
    q->data=x;
    q->link=NULL;
    NODE *p=head;
    while(p->link!=NULL)p=p->link;
    p->link=q;
    (head->data)++;
}

int _front(NODE *head)
{
    return head->link->data;
}

int _size(NODE *head)
{
    return head->data;
}

void _clear(NODE *head)
{
    head->data=0;
    head->link=NULL;
}

bool _empty(NODE *head)
{
    return (head->data==0);
}

void _operator(NODE *head1,NODE *head2)// 2赋值给1
{
    head1->data=0;
    head1->link=NULL;
    if(head2->data==0)return;
    NODE *p=head2->link;
    while(p!=NULL)
    {
        push(head1,p->data);
        p=p->link;
    }
}

void _swap(NODE *head1,NODE *head2)
{
    NODE *tmp=(NODE *)malloc(sizeof(NODE));
    _operator(tmp,b);
    _operator(b,a);
    _operator(a,tmp);
}

int main()
{
    a=(NODE *)malloc(sizeof(NODE));
    b=(NODE *)malloc(sizeof(NODE));
    a->data=0,a->link=NULL;
    b->data=0,b->link=NULL;
    cin>>Q;
    while(Q--)
    {
        cin>>op;
        if(op=="pop")
        {
            cin>>obj;
            if(obj=="a")pop(a);
            else pop(b);
        }
        else if(op=="push")
        {
            cin>>obj>>x;
            if(obj=="a")push(a,x);
            else push(b,x);
        }
        else if(op=="front")
        {
            cin>>obj;
            if(obj=="a"&&!_empty(a))cout<<_front(a)<<endl;
            else if(obj=="b"&&!_empty(b))cout<<_front(b)<<endl;
            else cout<<"ERRORn";
        }
        else if(op=="size")
        {
            cin>>obj;
            if(obj=="a")cout<<_size(a)<<endl;
            else cout<<_size(b)<<endl;
        }
        else if(op=="clear")
        {
            cin>>obj;
            if(obj=="a")_clear(a);
            else _clear(b);
        }
        else if(op=="empty")
        {
            cin>>obj;
            if(obj=="a")cout<<(_empty(a)?"YES":"NO")<<endl;
            else cout<<(_empty(b)?"YES":"NO")<<endl;
        }
        else if(op=="operator=")
        {
            cin>>obj;
            if(obj=="a")_operator(b,a);
            else _operator(a,b);
        }
        else if(op=="swap")_swap(a,b);
    }
    return 0;
}

ps: 所以当我以为我需要复习数据结构,其实我应该复习的是C++,也就是当我以为我没文化的程度只是欣赏不来文章,而实际上我根本就不识字...

当然学长 @哈哈哈哈哈哈 比我写的好多了,走这里 :)

https://zhuanlan.zhihu.com/p/210271150​zhuanlan.zhihu.com
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值