传引用,传地址,传值的区别与深入剖析

目录

传值,传地址,传引用区别:

引用特性:

常引用

使用场景

1、做参数

​编辑

2、做返回值

引用和指针的区别:

引用和指针的不同点:

总结:


传值,传地址,传引用区别:

传值,传地址都理解,传值就是将一个结果传回去,传地址则是将一个变量的地址传回去。那传引用是什么?

下面这位好汉是李逵,小名铁牛,江湖人称“黑旋风”,这些不同的名字都指代同一个人。

我们每个人都有正名,小时候有乳名,写信有笔名,朋友给我们取的绰号……这些也都是指代着同一个人,只是称呼换了一个而已,但本质没有变。

引用:引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

引用表示方法:类型& 引用变量名(对象名) = 引用实体;

为了更加直接的理解引用,我们来看一下下面第一段代码

注意:引用类型必须和引用实体是同种类型的

 可以看见两个的地址是一样的,没有开辟新的内存空间,是同一片空间的不同表示方式。

引用特性:

  1. 引用在定义时必须初始化

  2. 一个变量可以有多个引用

  3. 引用一旦引用一个实体,再不能引用其他实体


     

我们可以看到这里引用没有进行初始化就报错了

 

可以看到,对一个实体进行多次引用,但已然表示同一片空间,而且对引用变量再进行引用依然还是表示同一片空间

接下来大家可以看一下这一段代码的结果

int main()
{
    int a = 10;
    int& ra = a;
    cout << "a  -->" << &a << endl;
    cout << "ra -->" << &ra << endl;
    int b = 20;
    ra = b;
    cout << "ra -->" << &ra << endl;
​
​
    return 0;
}

 

他们还是指向同一片空间,这也证实了,引用不能引用其他实体,这也是引用与指针较大的一个区别

那么此刻,ra的值是多少呢?

接下来让我们看一下他的值

 

可以看到,虽然不能指向另一片空间,但为什么值却是b的值呢?是因为在这里是将b的值赋给ra,同时也赋给a,而并不是引用,所以ra的值也跟着改变了

 

 

常引用

那么引用出了引用变量,那能常引用吗?答案是肯定的。

接下来让我们看一段代码,大家来猜猜这段代码正确吗?

 

int main()
{
    int& a = 10;
    cout << "a = " << a << endl;
​
    return 0;
}

答案是错误的,这里的常引用指的不是引用常量,而是引用由const修饰的常变量,那么接下来让我们看以下两段段代码,大家来猜一猜看哪段代码是正确的。

// 代码1
int main()
{
    const int a = 10;
    int& ra = a;
    cout << "ra = " << ra << endl;
​
    return 0;
}
​
// 代码2
int main()
{
    const int a = 10;
    const int& ra = a;
    cout << "ra = " << ra << endl;
​
    return 0;
}

答案揭晓:第二段代码是正确的。

那么是为什么呢?const修饰的整形是不能修改的,而int& 代表着原类型是int型,是一个可修改的类型,就与常引用形成了矛盾,那么在进行常引用的时候,我们只要加上const进行修饰限制,此处就不会报错了。

使用场景

1、做参数

void Swap(int& a, int& b)
{
    int tmp = a;
    a = b;
    b = tmp;
}
​
int main()
{
    int a = 10;
    int b = 20;
    cout << "a = " << a << endl;
    cout << "b = " << b << endl << endl;
    Swap(a, b);
    cout << "a = " << a << endl;
    cout << "b = " << b << endl << endl;
    
​
    return 0;
}

 

 

2、做返回值

我们来看一下下段代码,来猜一下输出结果会是多少呢?

 

int& Add(int a, int b)
{
    int c = a + b;
    return c;
}
int main()
{
    int& ret = Add(1, 2);
    cout << "Add(1, 2) is :" << ret << endl;
    return 0;
}

答案是3

接下来我们再提升下难度,这段代码的结果是多少呢?

 

int& Add(int a, int b)
{
    int c = a + b;
    return c;
}
​
int main()
{
    int& ret = Add(1, 2);
    Add(3, 4);
    cout << "Add(1, 2) is :" << ret << endl;
    return 0;
}

诶?怎么成7了呢?ret是引用的Add(1, 2) 返回的实体啊,怎么成7了呢?接下来用一张图来阐述以下在这期间发生了什么。

 

那么接下来我们再来修改以下代码:

int& Add(int a, int b)
{
    int c = a + b;
    return c;
}
int main()
{
    int& ret = Add(1, 2);
    cout << "Add(1, 2) is :" << ret << endl;
    Add(3, 4);
    printf("\n");
    cout << "Add(1, 2) is :" << ret << endl;
​
    return 0;
}

此时的值又会发生什么变化呢?

 

为什么第二次的输出值变成随机值了呢?

通过上一幅图来分析

在第一次调用完,将栈帧释放后,此时的ret的引用实体c地址还没有被覆盖,第二次调用之后仍然没有被覆盖,但当此时再去调用printf的时候,系统也会创建栈帧,此时栈帧的大小与Add函数开辟的栈帧大小也绝大可能是不相同的,也就意味着,原先c处地址块极大概率会被覆盖,从而就诞生了随机值。

那么接下来我们一起看一下值传递与引用传递在时间上的差距:

 

struct A{ int a[10000]; };
void Func1(A a){};
void Func2(A& a){};
void TimeTest()
{
    A a;
​
    // 以值作为函数参数
    size_t begin1 = clock();
    for (size_t i = 0; i < 10000; ++i)
        Func1(a);
    size_t end1 = clock();
​
    // 以引用作为函数参数
    size_t begin2 = clock();
    for (size_t i = 0; i < 10000; ++i)
        Func2(a);
    size_t end2 = clock();
​
    // 分别计算两个函数运行结束后的时间
    cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
    cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}
int main()
{
    TimeTest(); 
    return 0;
}

时间差距还不是很明显,我们再将数据放大至10W

struct A{ int a[100000]; };

可以看见传值大时间消耗远远大于传引用的时间消耗,这是因为传值会将数据一个一个的保存在栈帧里面,以及开辟空间的时间消耗,因此增加了时间消耗,而传引用不会开辟新空间来保存数据,他指向原数据,因此没有开辟空间的时间消耗,所以传引用的时间消耗远远小于传值的时间消耗。

引用和指针的区别:

在语法上引用没有开辟新空间,而是别名,但是在底层汇编的角度上,引用也是一个特殊的指针,是按照指针的方式来实现的,其存储的地址引用实体的地址

引用和指针的不同点:

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。

  2. 引用在定义时必须初始化,指针没有要求

  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何 一个同类型实体

  4. 没有NULL引用,但有NULL指针

  5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32 位平台下占4个字节)

  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小

  7. 有多级指针,但是没有多级引用

  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理

  9. 引用比指针使用起来相对更安全

总结:

传值会创建独立空间,在函数调用结束释放栈帧的时候,会通过临时变量将值传送回来,其地址就具有了不可测性,一旦栈销毁就无法溯源,并且比传引用消耗更多时间与空间

传地址于传引用相比最大的区别就是写法上不同,引用能实现的功能指针也能实现

传引用可以通过返回值来修改原值,但是当引用实体地址被覆盖的时候,这种方法就会失误,会导致随机值。

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值