指针

指针(pointer)“指向(point to)“另外一种类型的复合类型。与引用类似,指针也实现了对其它对象的间接访问。
指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。
指针无需在定义时赋初值。在块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值
int *p1, p2; //p1和p2都是指向int型对象的指针
double dp, *dp2 //dp2都是指向double型对象的指针,dp是double型

获取对象的地址

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

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

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

指针值

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

  1. 指向一个对象
  2. 指向紧邻对象所占空间的下一个位置。
  3. 空指针,意味着指针没有指向任何对象。
  4. 无效指针,也就是上述情况之外其他值。
    试图拷贝或以其他方式访问无效指针的值都将引发错误。

    利用指针访问对象

    如果一个指针指向了一个对象,则允许使用解引用符(操作符 *)来访问该对象:

    int ival = 42;
    int *p = &ival;     //p存放着变量ival的地址,或者说p是指向ival的指针
    cout << *p << endl; //由符号*得到指针p所指的对象,输出42

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

    *p = 0;            //由符号*得到指针p所指的对象,即可经由p为变量ival的赋值
    cout << *p << endl; //输出0

    为 *p 赋值实际上是为了 p 所指的对象赋值。

    空指针

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

    int *p = nullptr;         //等价于int *p = 0;
    int *p = 0;               //直接将p2初始化为字面常量0
    //首先需要 #include cstdlib
    int *p3 =NULL;            //等价于int *p = 0;

    现在的C++程序最好使用nullptr,同时尽量避免使用NULL。

    赋值和指针

    引用本身并非一个对象。一旦定义了引用,就无法令其再绑定到另外的对象,之后每次使用这个引用都是访问它最初绑定的那个对象。
    指针和它存放的地址之间就没有这种限制了。和其他任何变量(只要不是引用)一样,给指针赋值就是令它存放一个新的地址,从而指向一个新的对象:

    
    int i = 42;
    int *pi = 0;    //pi被初始化,没有指向任何对象
    int *pi2 = &i;  //pi2被初始化,存有i的地址
    int *pi3;       //如果pi3定义于块内,则pi3的值是无法确定的

pi3 = pi2; //pi3和pi2指向同一个对象i
pi2 = 0; //现在pi2不指向任何对象了

## 其他指针操作
只要指针有一个合法值,就能将它用在条件表达式中。和采用算术值作为条件遵循的规则类似,如果指针的值是0,条件则取false:
```C++
int ival = 1024;
int *pi = 0;      //pi合法,是一个空指针
int *pi2 = &ival; //pi2是一个合法的指针,存放着ival的地址
if(pi)            //pi的值是0,因此条件存放的值是false
     //....
if(pi2)           //pi2指向ival,因此它的值不是0,条件的值是true
     //....

任何非0指针,对应的条件值都是true。

void* 指针

void*是一种特殊的指针类型,可以用于存放任意对象的地址。

double obj = 3.14, *pd = &obj;
                     //正确:void*能存放任意类型对象的地址
void *pv = &obj;     //obj可以是任意类型的对象
pv = pd;             //pv可以存放任意类型的指针

利用void 指针能做的事比较有限:拿它和别的指针比较、作为函数的输入或输出,或者赋值给另一个void 指针。不能直接操作void*指针所指的对象,因为我们并知道这个对象到底是什么类型,也就无法确定能在这个对象上做哪些操作。

指向指针的指针

通过 的个数可以区分指针的级别。也就是说,表示指向指针的指针,表示指向指针的指针的指针,以此类推:

int ival = 1024;
int *pi = &ival;  //pi指向一个int型的数
int **ppi = &pi;  //ppi指向一个int型的指针

此处pi是指向int型数的指针,而ppi是指向int型指针的指针,下图描述了它们之间的关系。

图片描述

解引用int型指针会得到一个int型的数,同样,解引用指向指针的指针会得到一个指针。此时为了访问最原始的那个对象,需要对指针的指针做两次解引用:

cout << "输出方式:\n"
     << "int类型输出:" << ival << "\n"
     << "指向ival的指针:" << *pi << "\n"
     << "指向指针的指针:" << **ppi
     << endl;

该程序使用三种不同的方式输出了变量ival的值:第一种直接输出;第二种通过int型指针pi输出;第三种两次解引用ppi,取得ival的值。

指向指针的引用

引用本不是一个对象,因此不能定义指向引用的指针。但指针是对象,所以存在对指针的引用:

int i = 42;
int *p;       //p是一个int型指针
int &r = *p;  //r是一个对指针p的引用

r = &i;       //r引用了一个指针,因此给r赋值&i就是令p指向1
*r = 0;       //解引用r得到i,也就是p指向的对象,将i的值改为0
最后修改:2024 年 03 月 17 日
如果觉得我的文章对你有用,请随意赞赏