C++:指针和引用

Wesley13
• 阅读 669

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

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;
}

运行结果如下: 

C++:指针和引用
 
由结果我们可以看出引用变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是采用引用的形式返回。
//我们可以看看汇编语言是如何这两种不同返回方式的,如下图:

C++:指针和引用
 
那么问题来了,我们有该如何选择以那种方式返回呢???

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

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

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

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

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

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

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

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

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

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
java 复制Map对象(深拷贝与浅拷贝)
java复制Map对象(深拷贝与浅拷贝)CreationTime2018年6月4日10点00分Author:Marydon1.深拷贝与浅拷贝  浅拷贝:只复制对象的引用,两个引用仍然指向同一个对象
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Kevin501 Kevin501
3年前
Go语言中new()和make()的区别
1.Go语言中的值类型和引用类型值类型:int,float,bool,string,struct和数组(数组要特别注意,别搞混了)变量直接存储值,分配栈区的内存空间,这些变量所占据的空间在函数被调用完后会自动释放。引用类型:slice,map,chan和值类型对应的指针变量存储的是一个地址(或者理解为指针),指针指向内存中真
Stella981 Stella981
3年前
PhoneGap设置Icon
参考:http://cordova.apache.org/docs/en/latest/config\_ref/images.html通过config.xml中的<icon标签来设置Icon<iconsrc"res/ios/icon.png"platform"ios"width"57"height"57"densi
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
C++学习_从C到C++
一、引用的概念和应用 1.引用的概念下面写法定义了一个引用,并将其初始化为引用某个变量。类型名&引用名某变量名;intn4;int&rn;//r引用了n,r的类型是int&某个变量的引用,等价于这个变量,相当于该变量的一个别
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
小万哥 小万哥
6个月前
C++ 引用和指针:内存地址、创建方法及应用解析
C引用和指针创建引用引用变量是对现有变量的“别名”,它是使用&运算符创建的:cstringfood"Pizza";//食物变量string&mealfood;//对food的引用现在,我们可以使用变量名food或引用名meal来引用食物变量:c
小万哥 小万哥
6个月前
C++ 解引用与函数基础:内存地址、调用方法及声明
C解引用获取内存地址和值在上一页的示例中,我们使用了指针变量来获取变量的内存地址(与引用运算符&一起使用)。但是,你也可以使用指针来获取变量的值,这可以通过使用运算符(解引用运算符)来实现:cstringfood"Pizza";//变量声明stri