指针与引用的那些事儿

指针与引用的那些事儿

写在前面

说起指针,我对它真的是又爱有怕啊=。= 之所以爱,是发现很多时候指针操作真的很方便啊有木有,之所以怕,好吧,我承认归根到底还是因为自己理解得不够透彻,对指针的特性理解得不够深刻。但咱不能因为怕就放弃爱的东西吧,所以立志要循序渐进,步步为营。
之所以扯到引用是因为引用常常伴随着指针出现,两者真是好基友啊。。。既然做个小结,就一起看了哈

指针

在我粗浅的理解中,指针,就是一个地址,它存放的是它所指向的对象的地址值。《C++ Primer》中指出:“指针用于指向对象”。也就是说,指针提供了一种访问它所指向对象的方式,因为它保存着它指向对象的地址。例如:

    int a = 10;
    int *pa = &a;

其中,pa就是一个指针,它指向a。a是一个整型变量,相应的,pa是一个整型的指针。
第一行中*pa中的 *操作符表明pa是一个指针变量。
第二行中,&a表示对a取地址。取地址操作符只能用于左值。从第二行可以看出,我们将a的地址作为pa的初始值,因此pa保存的是a的地址。
当我们要理解一个指针时,可以从右向左阅读指针的声明语句,如:

    double *dp;
    char *cp;
    string *pstring;

分别分析一下这三个指针。按照从右往左的阅读顺序,我们知道,dp是一个指向double类型的指针,cp是一个指向char类型的指针,而pstring是一个指向string的指针。

指针的声明形式
在声明指针的时候,*号的位置可以在类型标识符和指针名之间的任意位置,下面的三种方式是等价的(注意查看 * 号位置)

    double *dp;
    double* dp;
    double * dp;

指针的初始化
对指针进行初始化或赋值只能使用下面四种类型的值:
(1)0值,或在编译时可获得0值的整型const对象或字面值常量0;
(2)类型匹配的对象的地址
(3)另一对象之后的下一地址
(4)同类型的另一个有效指针

    int a;
    int zero = 0;
    const int cval = 0;

    int *p = a;//错误,不能用int型变量给int型指针赋值
    p = zero;//错误,尽管zero值为0,但不是在编译时确定的
    p = cval;//正确,cval是一个const常量,其值在编译时已经确定
    p = 0;//正确,直接用数值0初始化
    p = 1;//错误,不能用除0以外的数值常量对指针赋值
    p = NULL;//正确,NULL是一个宏定义,值为0,在编译时确定

指针解引用
对指针进行解引用可以访问指针所指向的对象,解引用操作符为*,如

    int a = 10;
    int *pa = &a;
    cout<<*pa<<endl;

最后一句对指针pa进行了解引用操作,因此将获得pa指向的对象(a)的值,所以输出10.
对指针解引用之后得到的是一个左值,因此可以通过操作指针来修改指针指向对象的值,如:

    int a = 10;
    int *pa = &a;

    *pa = 15;
    cout<<a<<endl;

输出的结果是15. 此外,也可以修改指针本身的值,改变该指针的指向,如:

    int a = 10;
    int *pa = &a;

    int b = 12;
    pa = &b;//修改指针本身指向

另外,在指针上还涉及到常量指针和指针常量
指向常量的指针
如果指针指向const对象,则不允许用指针来改变其所指向的const值。如:

const int *p;
int const *p;

两者等价,p是一个指向int类型const对象的指针,const限定的是p指向的对象类型,而不是p本身。即p不是const的,它的指向可以改变
例如:

const int a = 1;//const常量
const int *p;//指向const常量的非const指针
p = &a;//给指向const常量的指针赋值,让其指向const常量
*p = 2;//错误,不能通过p改变其指向的内容

int b = 3;//非const常量
p = &b;//可以改变p的指向,让其指向非const常量
*p = 4;//错误,不能通过p改变其指向内容,哪怕它指向的内容不是const常量

总结:把指向const的指针理解为“自以为指向const的指针”
指针常量(const指针)
const指针是本身不能修改其指向的指针,必须在第一次声明时就初始化。指针是const并没有说明是否能使用该指针修改它所指向对象的值。指针所指对象的值能否修改完全取决于该对象的类型。

int a = 1;
int *const p = &a;
p = p;//错误,不能改变p的指向,哪怕是赋回同样的值
*p = 2;//正确,p指向的对象a不是const类型,因此可以通过p改变其内容

//注意比较
int a = 1;
int const *p = &a;//常量指针,不可通过p改变a的值,即使a不是常量
const int *p = &a;//常量指针,与上句等价
int *const p = &a;//指针常量,p不可改变指向

本质:const放在谁后面谁就不可修改,const在最前面则将其后移一位即可,二者等效
指向const对象的const指针
这种情况下,指针指向的内容不可变,其指针本身的指向也不可变,如:
int const a = 1;
const int *const p = &a;//指向const对象的const指针,p和a都不可变

总结:
1)、const在前面

const int nValue; // nValue是const 
const char *pContent; //*pContent是const, pContent可变
const (char *) pContent;//pContent是const,*pContent可变
char* const pContent; //pContent是const,*pContent可变
const char* const pContent; //pContent和*pContent都是const

2)、const在后面,与上面的声明对等

int const nValue;// nValue是const
char const * pContent;// *pContent是const, pContent可变
(char *) const pContent;//pContent是const,*pContent可变
char* const pContent;// pContent是const,*pContent可变
char const* const pContent;// pContent和*pContent都是const

引用

引用就是对象的别名。例如某个人的名字叫张哈哈,他排行老三,大家都叫他张三,那么张三就是张哈哈的别名(外号),张三和张哈哈是同一个人。引用大致就是这么个意思。
在变量名的前添加“&”符号就把该变量定义为一个引用。引用实际上是一个“复合类型”,在定义引用的时候,必须声明该引用是哪种数据类型(int、double等)的引用,不能定义引用类型的引用,此外,引用在声明的时候必须进行初始化,(不然只给出一个外号,并不知道是谁的外号),如:

    int a = 10;
    int &refa1 = a;//正确,将refa声明为a的引用,相当于a是张哈哈,refa是张三
    int &refa2;//错误,没有进行初始化,不知道是谁的别名
    int &refa3 = 0;//错误,必须用对象(变量)初始化,不能用数值

既然引用是别名,那对引用的所有操作都将直接作用在引用绑定的对象上

    int a = 10;
    int &refa1 = a;

    refa1++;
    cout<<a<<endl;

输出结果为11.
需要注意的是,当引用初始化后,只要该引用存在,它就保持绑定到初始化时指向的对象。不可能将引用绑定到另一个引用上。
直接看例子:

    int a = 10;
    int &refa1 = a;

    int b = 12;
    &refa1 = b;//错误,不能给引用赋新值

实际上,对于&refa1 = b,从语法上分析也是不正确的,因为等号左边是int*, 而右边是int。那么是否将右边改为&b就正确了呢?

   &refa1 = &b;//错误,左操作数必须为左值

C++中没有提供改变引用的写法
另外,对不能给引用赋新值的理解容易出现错误,下面两种方式并不是在给引用赋新值:

    int a = 10;
    int &refa1 = a;
    refa1 = 13;//正确

这里refa1 = 13相当于是通过引用改变原始变量的值,也就是说,我们让张三有13块钱,那么张哈哈也就有13块钱了。下面是转了个弯的写法

    int a = 10;
    int &refa1 = a;
    
    int b = 13;
    refa1 = b;

实际上就是等量代换了一下咯, b = 13, refa1 = b -> refa1 = 13.
const引用
const引用是指向const对象的引用,如

    const int a = 10;
    const int &refa = a;//正确

    int &refa2 = a;//错误

对于int &refa2 = a错误的原因,我们也很容易理解,因为refa2是非const的,那么我就改变refa2的值,这样a的值也就相应改变,但是a是const的,其值是不允许更改的。这就出现了矛盾,因此是错误的。
对于const引用,它的初始化有点特别,它可以初始化为不同类型的对象或者初始化为右值

    int a = 10;
    const int &refa = a;//正确

    refa = 12;//错误,因为refa是const的
    a = 12;//正确,a并非const

上述的例子说明,不能通过const引用修改其绑定到的对象的值,尽管其绑定到的对象并非const。同指向const的指针的理解相似,我们可以把指向const的引用理解为:自以为指向const的引用

    int a = 10;
    const int &ref = 42 ;//正确
    const int &ref2 = a +2;//正确

上述例子说明,const类型的引用可以用右值(如字面常量)初始化,但非const类型的引用就不行。因为右值都是只读的,这和const引用的特性是兼容的。

    double dval = 3.14;
    const int &ref = dval;//正确

上述列子说明,const类型的引用可以初始化为不同类型的对象,这种用法的使用场景很少见
总结:非const引用只能绑定到与该引用同类型的对象,
const引用则可以绑定到不同但相关联的类型的对象,或绑定到右值

指针和引用的比较

相同点
指针和引用都是地址,指针指向一块内存,它的值是所值内存的地址;内存是某块内存的别名
不同点
最重要的两个区别:
(1)引用总是指向某个对象:定义应用时没有初始化是错误的
(2)赋值行为的差异:给引用赋值修改的是该引用所关联的对象的值,而并不是使引用与另一个对象关联
此外,还有以下区别:
(3)指针可以为NULL,引用不可以(这点是(1)的扩展)
(4)指针可以有多级指针,但引用没有多级引用
(5)“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小

转载于:https://www.cnblogs.com/scut-linmaojiang/p/5328245.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值