指针
指针(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种状态之一:
- 指向一个对象
- 指向紧邻对象所占空间的下一个位置。
- 空指针,意味着指针没有指向任何对象。
- 无效指针,也就是上述情况之外其他值。
试图拷贝或以其他方式访问无效指针的值都将引发错误。利用指针访问对象
如果一个指针指向了一个对象,则允许使用解引用符(操作符 *)来访问该对象:
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 = π //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