c、c++ 引用与指针

引用

我们常用的引用都是左值引用,而什么是引用呢,所为的引用其实就是给对象起了另一个名字。要注意的是引用不是对象,同时,引用必须要进行初始化。

看下代码吧:

int ival = 1024;
int &refval = ival; // 用 & 来表示refval是ival的引用
int &refval2; // 这里会报错,因为引用必须初始化

为什么必须进行初始化呢?因为在定义引用的时候,程序把引用和他的初始值绑定在一起,记住,绑定不是拷贝,也就是说如果你对利用引用修改值,那么被引用的对象的值也会发生变化。并且,引用一旦绑定之后就不可更改,这也就是为什么引用必须初始化的原因。

//用代码举个例子吧

int ival = 1024;
int &refval = ival;
refval = 3;
cout<<ival<<endl;//这里的输出结果是三,说明ival的值也被改变了

引用的定义

允许在一条语句中定义多个引用,当然,每个引用的标志符都必须以&开头:

int i = 1, i2 = 2;//定义两个普通的int类型
int &j = i, k = i2;//其中j是对I的引用,k是普通的int变量
int &m = i, &n = m;//m是对I的引用,而n是对i的引用的引用,其实就是这个意思,给小李取了一个外号叫
                   //李子,然后又给李子取了个外号叫木子
&j = i2;//error:Expression is not assignable

指针

指针是指向另外一种类型的复合类型,用来存放对象的地址。和引用相似,指针也实现的对其他对象的简介访问。然而指针和引用相比有着许多的不同之处。首先,指针本身就是一个对象,由声明符*和变量名组成如:*p。所以指针可以不初始化(但这样做会出现问题,后面再讲,不过编译器是不会报错的),因此也允许对齐进行赋值和拷贝。第二,指针所指向的对象可以随时改变。

    int i  = 1;
    int *p = &i; // 这里的&是去地址符。不是引用的意思,表示p存放i的地址
    int *p1,*p2; // 编译器不会报错,因为指针可以不初始化
    p1 = p;      // p1和p同时指向i

形如p2,并没有被初始化,这首p2 就会变成一个野指针,p2的默认值是随机,我们也不知道p2指向的是内存中的哪个位置。野指针的危害就还请百度吧,这里就不多说了。

利用指针访问对象

如果指针指向了一个对象,可以使用解引用符* 来访问该对象。

可能会有疑惑,如同引用& 和地址&,指针*和解引用*,其实代表不是同样的东西,只是写法相同罢了(我就是这么理解的,具体代表什么意思看情况而定,应该还是好分辨的)

比如我们要输出i的值,可以这样:

cout<<*p<<endl;
int &k = *p;//这里的*也是解引用的意思
cout<<k<<endl;//这样也是可以的

通过解引用符*来获取p所存的地址的对象的值。

空指针

空指针不指向任何对象,在试图使用一个指针之前代码可以首先检查它是否为空。以下列出几个生成空指针的方法:

    int *p = nullptr;//c++11的一种新方法,就是空的意思
    int *p1 = 0;//直接初始化为字面常量0
    int *p2 = NULL;//其实和上面等于0时一样的

其他指针操作

    int ival = 11;
    int *p1 = &ival;
    int *p2 = 0;
    if(p1)
    {
        cout<<"p1 is true"<<endl;
    }
    if(p2)
    {
        cout<<"p2 is true"<<endl;
    }
    //输出结果
    //p1 is true

原因就是,如果指针的值是0,条件取false,否则,全部为true。这里说一下 ,如果是nullptr的话,直接理解空和非空就好了。

void* 指针

void* 是一种特殊的指针类型,用于存放任何对象的地址。但是,我们永远无法理解该地址的对象是什么类型的,这也就导致我们无法直接操作void*指针所指的对象,换句话说就是问无法通过void*来访问内存空间中所存储的对象。

    int obj = 2;
    double * p2 = 0;
    void* p3 = &obj;
    p3 = p2;

指向指针的指针

先说一下变量吧,变量的定义包括一个基本的数据类型和一组声明符(比如 int *p , int是数据类型,*是声明符中的修饰符,p是名字)。并且声明符中的修饰符的个数并没有限制。如下:

    int ival = 22;
    int *p = &ival; //p指向ival
    int **pp = &p; //pp 指向一个int形的指针
    int ***ppp = &pp; //ppp 指向一个指向int型的指针的指针

如果想输出ival的话也有很多方法:

    cout<<*p<<endl;
    cout<<**pp<<endl;
    cout<<***ppp<<endl;
    //上面的*全部都是解引用的意思

指向指针的引用

上面也说过了,引用本身并不是一个对象,而只是一个别名(外号),因此不能定义指向引用的指针,但指针是一个对象,所以存在对指针的引用。是不是有点绕,所以看代码可能会好理解点。

    int ival = 11;
    int *p = &ival;//这句话其实可以拆成两句话。int *p, p = &ival;
    int **pp = &p;
    int *&r = p; //从右向左读吧,其中&代表r是一个引用,*代表其是对指针的引用
    int **&rr = pp;//一样的道理 **代表其事对指针的指针的引用
    *r = 22;
    cout<<ival<<endl;//输出结果为 22
    **rr = 33;
    cout<<ival<<endl;//输出结果为 33

总结:

以上内容许多是参考c++primer 这本书的,也别说抄袭了,因为这篇博客更多的是一种学习笔记吧,全部手打的。博主本人也水平有限,大概加了一点自己的尝试和想法,希望共同进步吧。

猜你喜欢

转载自blog.csdn.net/monster_acm/article/details/81218193