引用与指针

引用与指针

1、引用

引用(reference)为对象起了另外一个名字,引用类型引用(refer to)另外一种类型。通过将声明符写出&d的形式来定义引用类型,其中d是声明的变量名。

一般在初始化变量时,初始值会被拷贝到新建的对象中,然后定义引用时,程序把引用和它的初始值绑定在一起,而不是将初始值拷贝给引用。一旦初始化完成,引用将和它的初始值一直绑定在一起。因为无法令引用重新绑定到另外一个对象,因此引用必须初始化。

引用即别名。引用并非对象,相反的,它只是为一个已经存在的对象所起的另外一个名字。

int val = 1024;

int &refval1 = val;  //refval指向val(是val的另外一个名字)

int &refval2; //报错:引用必须初始化

 

定义一个引用之后,对其进行的所有操作都是在与之绑定的对象上进行的。为引用赋值,实际上是把值赋给了与引用绑定的对象;获取引用的值,实际上是获取了与引用绑定的对象的值;以引用作为初始值,实际上是以与引用绑定的对象作为初始值。

refval1 = 2;  //把2赋给refval1所指向的对象,此处即赋给了val

int ii = refval1;  //ii被初始化为val的值,与ii = val;执行结果相同

int &refval3 = refval1;  //refval3绑定到了那个鱼refval1绑定的对象上,即绑定到val上

因为引用本身不是一个对象,所以不能定义引用的引用。

 

允许在一条语句中定义多个引用,其中每个引用标识符都必须以&开头。

几乎所有引用的类型都要和与之绑定的对象严格匹配。

引用只能绑定在对象上,不能与字面值或某个表达式的计算结果绑定到一起。

int i = 1024, i2 = 2048;   //i和i2都是int

int &r = i, r2 = i2;  //r是一个引用,与i绑定到一起,r2是一个int

int i3 = 1024, &r3 = i3;  //i3是int,r3是一个引用,与i3绑定在一起

int &r3 = i3, &r4 = i2;  //r3和r4都是引用

int &refval4 = 10;  //错误:引用类型的初始值必须是一个对象

double dval =3.14;

int &refval5 = dval;  //错误:此处引用类型的初始值必须是int型对象

 

2、指针

定义:

指针(pointer)是指向(point to)另外一种类型的复合类型。定义指针的类型的方法是将声明符写成*d的形式,其中d是变量名。如果在一条语句中定义了多个指针变量,每个变量前面都必须有符号。

int *ip1, *ip2;  // ip1和ip2均为指向int型对象的指针

double dp, **dp2;  //dp2为指向double型对象的指针,dp为double型对象

 

获取对象的地址

指针存放某个对象的地址,获取该地址需要使用取地址符(&)

在声明语句中指针的类型实际上被用于指定它所指向对象的类型,所以除了两种类型意外,其他所有指针的类型都要和它所指向的对象类型严格匹配

因为引用不是对象,没有实际的地址,所以不能定义指向引用的指针

int ival = 42, &ref = ival; 

int *p = &ival;  //p存放变量ival的地址,或者说p是指向变量ival的指针

int *p1 = &ref;  //错误:ref为引用,没有地址,不能定义指向它的指针

double dval; 

double *pd = &dval;  //初始值double类型对象的地址

double *pd2 = pd;  //初始值是指向double对象的指针

int *pi = pd;  //错误:指针pi的类型和pd的类型是不匹配的

pi = &dval;  //错误:试图将double型对象的地址赋给int型指针

 

指针的值

指针的值(即地址)应属于以下4中状态之一:

  1. 指向一个对象
  2. 指向紧邻对象所占空间的下一个位置
  3. 空指针,意味着没有指向任何对象
  4. 无效指针,也就是上述情况之外的其他值

试图拷贝或以其他方式访问无效指针将引发错误,编译器不负责检查此类错误。

第2和第3种形式的指针虽然有效,但是由于没有指向任何具体对象,所以试图访问此类指针对象的行为也不被允许。

 

利用指针访问对象

指针指向一个对象,则允许使用解引用符(*)来访问该对象。

对指针解引用会得到所指的对象,因此如果给解引用的结果赋值,实际上也就是给指针所指的对象赋值。

解引用操作仅适用于那些确实指向了某个对象的有效指针

int ival = 42;  //

int *p = &ival;  //p存放着变量ival的地址,或者说p是指向变量ival的指针

cout << *p;  //由符号*得到指针p所指的对象,输出42

*p = 0;  //由符号*得到p所指的对象,即可由p为变量ival赋值

在声明语句中,&*用于组成复合类型,在表达式中,他们是运算符

 

空指针

空指针不指向任何对象,在试图使用一个指针之前,可以先检查一个它是否为空。

生成空指针的方法:

int *p = nullptr; //等价于第二个,nullptr是一种特殊类型的字面值,可以被转换成任意其他类型的指针。

int *p2 = 0;  //直接将p2初始化为字面常量0

int *p3 = NULL;  //NULL 为预处理变量,它的值就为0,需要首先#include cstdlib

int变量直接赋值给指针是错误的操作,即使int变量的值恰好等于0也不行。

int zero = 0;

pi = zero; //错误:不能把int变量直接赋给指针

和其他变量一样,访问未经初始化的指针所引发的后果是无法预测的,通常这一行为将会造成程序崩溃。

 

赋值和指针

和其他非引用的任何变量一样,给指针赋值就是令它存放一个新的地址,从而指向一个新的对象。

赋值永远改变的是等号左侧的对象

int i = 42;

int *pi = 0;  //pi被初始化,但是没有指向任何对象

int *pi2 = &i;  //pi2被初始化,存有i的地址

int *pi3;  //如果pi3存放在块内,则pi3的值无法确定

pi3 = pi2;  //pi2与pi3指向同一个对象i

*pi2 = 50;  //pi2指向的i的值被改变,指针pi2并没有改变

pi2 = 0;  //pi2的值被改变,现在pi2不指向任何对象了

 

其他指针操作

只要指针拥有一个合法值,就可以用在条件表达式中,和采用算术值作为条件遵循的规则类似。任何非0指针对应的条件值都是true,0指针条件去false。

对于两个类型相同的指针,可以用相等操作符(==)和不相等操作符(!=)来比较她们,比较的结果是布尔类型。

 

void*指针

void*是一种特殊的指针类型,可用于存放任意对象的地址,该地址中到底是个什么类型的变量并不了解。

void*指针能做的事情很有限:和别的指针比较、作为函数的输入或输出、赋给另外一个void*指针。

不能直接操作void*指针所指的对象

 

指针与引用的区别

★ 相同点:

    1. 都是地址的概念;

    指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名。

★ 区别:

    1. 指针是一个变量,而引用仅是个别名;

    2. 引用使用时无需解引用(*),指针需要解引用;

    3. 引用只能在定义时被初始化一次,之后不可变;指针可变;

    4. 引用没有 const,指针有 const,const 的指针不可变;

    5. 引用不能为空,指针可以为空;

    6. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;typeid(T) == typeid(T&) 恒为真,sizeof(T) == sizeof(T&) 恒为真,但是当引用作为成员时,其占用空间与指针相同(没找到标准的规定)。

7. 指针和引用的自增(++)运算意义不一样;

8. 如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄漏;

    ★ 联系

    1. 引用在语言内部用指针实现(const 指针?)。

    2. 对一般应用而言,把引用理解为指针,不会犯严重语义错误。引用是操作受限了的指针(仅容许取内容操作)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值