zoukankan      html  css  js  c++  java
  • 指针与引用的那些事儿

    指针与引用的那些事儿

    写在前面

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

    指针

    在我粗浅的理解中,指针,就是一个地址,它存放的是它所指向的对象的地址值。《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 指针”得到的是指针本身(所指向的变量或对象的地址)的大小

  • 相关阅读:
    shell脚本的常用参数
    Qt中使用Protobuf简单案例(Windows + msvc)
    使用PicGo和Typora写Markdown
    CentOS7安装protobuf(C++)和简单使用
    protobuf编译、安装和简单使用C++ (Windows+VS平台)
    protocol buffers 文档(一)-语法指导
    Base64编码和其在图片的传输的应用
    Qt程序打包发布
    Qt中的Label和PushButton背景图自动缩放设置
    TCP的粘包和拆包问题及解决
  • 原文地址:https://www.cnblogs.com/scut-linmaojiang/p/5328245.html
Copyright © 2011-2022 走看看