C++:指针和引用

引用的概念及用法 
所谓的引用并不是说重新定义的一个新的变量,而是给一个已经定义好了的变量起的一个别名。 
下面看看引用到底是如何使用的:

void test1()
{
    int a = 1;
    int& b = a; //引用变量b是a的别名

    std::cout<<"a:address->"<<&a<<std::endl;
    std::cout<<"A:address->"<<&b<<std::endl;  //注意这里的&是取地址

    a = 10;
    b = 100;
    std::cout<<"a = "<<a<<std::endl;
    std::cout<<"b = "<<b<<std::endl;

    int& c = b; //应用变量c是引用变量b的别名,别名的别名
    c = 1000;
    std::cout<<"a = "<<a<<std::endl;
    std::cout<<"b = "<<b<<std::endl;
    std::cout<<"c = "<<c<<std::endl;
}

运行结果如下: 


 
由结果我们可以看出引用变m量b与变量a的地址是一样的,该变b的值也会影响a。并且一个变量可以取多个别名,这里b是a的别名,c是b的别名,也就是a的别名了。就像我们人一样,你有一个大名(身份证上的名字),可能还会有一个小名,也或许还会给自己起一个洋气的英文名,总之不管别人叫哪一个名字,叫的都是你本人就对了。

总结: 
1、一个变量可以有多个别名 
2、引用必须初始化b 
3、引用只能在初始化的时候引用一次,之后不能再引用其他的变量

引用做参数 
在之前的学习当中,我们知道调用函数的传参有传值调用和传址调用。下面再来看一看这两种方式:

1、传值调用

void swap(int a,in b)
{
    int tmp = a;
    a = b;
    b = tmp;
}
//现在的我们都知道了这样的函数是无法完成我们希望的交换功能的。
//究其原因,就是因为这里使用的是传值方式,那么如果别人一旦想要
//调用我给我传两个参数,我就要生成两个局部的临时变量用来接收
//别人给我传的两个参数。但是当调用结束,函数栈帧释放,相应的
//用于接收参数的两个局部变量也会被一同释放掉,但是交换是发生在
//这两个局部变量之间的,现在他们已经被释放掉了,并且从头到尾
//都没有对调用方的两个想要交换的变量产生任何影响。因此这里的
//传值调用并完成不了交换的功能。

2、传址调用

void swap(int *a,int *b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}
//所谓的传址调用就是传指针。
//现在我们将两个形参改为指针,也就是说别人想要调用我完成交换功能时
//就将想要交换的两个变量的地址传给我就好了。函数调用期间对地址里面
//的内容进行交换,即便调用结束以后,函数栈帧被释放,但是是对两个地址
//的内容进行了交换,释放栈帧以后这两个地址并不是函数栈帧里的,
//所以并不会一并被释放,并且完成了交换的功能。

3、传引用

void swap(int& a,int& b)
{
    int tmp = a;
    a = b;
    b = tmp;
}
//我们知道引用变量就是我们给一个已经定义好的变量起的一个别名,
//所以说,如果我们这里采用传引用的方式,那我们这里的形参就是实参的别名
//在刚刚我们也看了,变量和变量的别名,他们两个的地址是同一个,所以啊,
//这和传址调用有着异曲同工之妙。

引用做返回值

有时候我们一个函数调用结束需要返回一些信息供调用方使用。比如说一个加法函数。

//方法一
int ADD(int a,int b)
{
    int ret = a+b;
    return ret;
}
//方法二
int& ADD(int a,int b)
{
    int ret = a+b;
    return ret;
}
//这里方法一是采用值的形式返回,而方法2是采用引用的形式返回。
//我们可以看看汇编语言是如何这两种不同返回方式的,如下图:

 


 
那么问题来了,我们有该如何选择以那种方式返回呢???

1、如果返回的对象出了该函数作用域依旧存在,则使用引用返回,因为这样会更加高效 
2、如果返回对象处了函数的作用域就不存在了,则使用值返回。 
注意:不要返回一个临时变量的引用,因为临时变量在函数调用结束以后会随着栈帧的释放而被释放,而传引用返回的方式返回的是变量的地址,而事实是该变量已经被释放。

引用和指针的区别 
在这之前我们一直在说,引用是一个变量的别名,所以可能就会想到说这个引用变量时不会占据任何的空间的。但是!请注意!这种想法是不对的。引用变量也是会占据一定的内存空间的,也需要在栈上额外占用存储空间。 
因为引用的底层实现其实是指针。从语法上来看只是一个别名,但在底层上依旧是开辟了一块空间。

int main()
{
    int a = 0;
    int& b = a;
    return 0;
}

看一下这段代码的汇编:是如何处理变量a,和引用变量b 


 
从汇编我们可以看出对引用变量初始化为a的别名,就是将a的地址给了引用变量b。想一想这种方式是不是很熟悉?对了,正如你所想到的我们经常写的一个代码:

int a = 0;
int *p = &a;
//取a的地址赋给指针变量p

这样看来其实引用的底层也就是一个指针,只不过明面上向我们所展示的是一个变量的别名,但我们应该注意引用变量是一个已经定义过的变量的别名,他是别名,他也占空间,因为他的底层实现是指针。

下面就看一看引用和指针的区别:

1、引用只能在定义时初始化一次,之后不能改变指向其他的变量,但指针可以。 
2、引用必须指向有效的变量,但指针可以为空。 
3、sizeof引用得到的是所指向的变量的大小,但sizeof指针是对象的地址的大小。 
4、引用的自增(+ +)自减(- -)是对值的+1或-1,而指针++或–是+或-其所指向的类型大小。
 

转载于:https://my.oschina.net/u/920274/blog/3073955

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值