# 指针 指针(pointer)“指向(point to)“另外一种类型的复合类型。与引用类似,指针也实现了对其它对象的间接访问。 指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。 指针无需在定义时赋初值。在块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值。 `int *p1, p2;` //p1和p2都是指向int型对象的指针 `double dp, *dp2` //dp2都是指向double型对象的指针,dp是double型 ## 获取对象的地址 指针存放着某个对象的地址,想要获取该地址,需要使用取址符(操作符&): ```C++ int ival = 42; int *p = &ival; //p存放变量ival的地址,或者说p是指向ival的指针 ``` 因为引用不是对象,没有实际地址,所以不能定义指向引用的指针。 ## 指针值 指针的值(即地址)应属于下列4种状态之一: 1. 指向一个对象 2. 指向紧邻对象所占空间的下一个位置。 3. 空指针,意味着指针没有指向任何对象。 4. 无效指针,也就是上述情况之外其他值。 试图拷贝或以其他方式访问无效指针的值都将引发错误。 ## 利用指针访问对象 如果一个指针指向了一个对象,则允许使用解引用符(操作符 *)来访问该对象: ```C++ int ival = 42; int *p = &ival; //p存放着变量ival的地址,或者说p是指向ival的指针 cout << *p << endl; //由符号*得到指针p所指的对象,输出42 ``` 对指针解引用会得出所指的对象,因此如果给解引用的结果赋值,实际上也就是给制作所指的对象赋值: ```C++ *p = 0; //由符号*得到指针p所指的对象,即可经由p为变量ival的赋值 cout << *p << endl; //输出0 ``` 为 *p 赋值实际上是为了 p 所指的对象赋值。 ## 空指针 空指针(null pointer)不指向任何对象,在试图使用一个指针代码可以首先检查它是否为空。以下列出几个生成空指针的方法: ```C++ int *p = nullptr; //等价于int *p = 0; int *p = 0; //直接将p2初始化为字面常量0 //首先需要 #include cstdlib int *p3 =NULL; //等价于int *p = 0; ``` 现在的C++程序最好使用nullptr,同时尽量避免使用NULL。 ## 赋值和指针 引用本身并非一个对象。一旦定义了引用,就无法令其再绑定到另外的对象,之后每次使用这个引用都是访问它最初绑定的那个对象。 指针和它存放的地址之间就没有这种限制了。和其他任何变量(只要不是引用)一样,给指针赋值就是令它存放一个新的地址,从而指向一个新的对象: ```C++ 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*是一种特殊的指针类型,可以用于存放任意对象的地址。 ```C++ double obj = 3.14, *pd = &obj; //正确:void*能存放任意类型对象的地址 void *pv = &obj; //obj可以是任意类型的对象 pv = pd; //pv可以存放任意类型的指针 ``` 利用void* 指针能做的事比较有限:拿它和别的指针比较、作为函数的输入或输出,或者赋值给另一个void* 指针。不能直接操作void*指针所指的对象,因为我们并知道这个对象到底是什么类型,也就无法确定能在这个对象上做哪些操作。 ## 指向指针的指针 通过* 的个数可以区分指针的级别。也就是说,**表示指向指针的指针,***表示指向指针的指针的指针,以此类推: ```C++ int ival = 1024; int *pi = &ival; //pi指向一个int型的数 int **ppi = π //ppi指向一个int型的指针 ``` 此处pi是指向int型数的指针,而ppi是指向int型指针的指针,下图描述了它们之间的关系。  解引用int型指针会得到一个int型的数,同样,解引用指向指针的指针会得到一个指针。此时为了访问最原始的那个对象,需要对指针的指针做两次解引用: ```C++ cout << "输出方式:\n" << "int类型输出:" << ival << "\n" << "指向ival的指针:" << *pi << "\n" << "指向指针的指针:" << **ppi << endl; ``` 该程序使用三种不同的方式输出了变量ival的值:第一种直接输出;第二种通过int型指针pi输出;第三种两次解引用ppi,取得ival的值。 ## 指向指针的引用 引用本不是一个对象,因此不能定义指向引用的指针。但指针是对象,所以存在对指针的引用: ```C++ 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 ``` Loading... # 指针 指针(pointer)“指向(point to)“另外一种类型的复合类型。与引用类似,指针也实现了对其它对象的间接访问。 指针本身就是一个对象,<mark>允许</mark>对指针<mark>赋值和拷贝</mark>,而且在指针的生命周期内它可以先后指向几个不同的对象。 指针<mark>无需</mark>在定义时赋初值。在块作用域内定义的指针如果没有被初始化,也将拥有一个<mark>不确定的值</mark>。 `int *p1, p2;` //p1和p2都是指向int型对象的指针 `double dp, *dp2` //dp2都是指向double型对象的指针,dp是double型 ## 获取对象的地址 指针<mark>存放着某个对象的地址</mark>,想要获取该地址,需要使用<mark>取址符(操作符&)</mark>: ```C++ int ival = 42; int *p = &ival; //p存放变量ival的地址,或者说p是指向ival的指针 ``` 因为引用不是对象,<mark>没有实际地址</mark>,所以不能定义指向引用的指针。 ## 指针值 指针的值(即地址)应属于下列4种状态之一: 1. 指向一个对象 2. 指向紧邻对象所占空间的下一个位置。 3. 空指针,意味着指针没有指向任何对象。 4. 无效指针,也就是上述情况之外其他值。 试图拷贝或以其他方式访问无效指针的值都将引发错误。 ## 利用指针访问对象 如果一个指针指向了一个对象,则允许使用<mark>解引用符(操作符 *)</mark>来访问该对象: ```C++ int ival = 42; int *p = &ival; //p存放着变量ival的地址,或者说p是指向ival的指针 cout << *p << endl; //由符号*得到指针p所指的对象,输出42 ``` 对指针解引用会得出所指的对象,因此如果给解引用的结果赋值,实际上也就是给制作所指的对象赋值: ```C++ *p = 0; //由符号*得到指针p所指的对象,即可经由p为变量ival的赋值 cout << *p << endl; //输出0 ``` 为 *p 赋值实际上是为了 p 所指的对象赋值。 ## 空指针 空指针(null pointer)不指向任何对象,在试图使用一个指针代码可以首先检查它是否为空。以下列出几个生成空指针的方法: ```C++ int *p = nullptr; //等价于int *p = 0; int *p = 0; //直接将p2初始化为字面常量0 //首先需要 #include cstdlib int *p3 =NULL; //等价于int *p = 0; ``` 现在的C++程序最好使用nullptr,同时尽量避免使用NULL。 ## 赋值和指针 引用本身并非一个对象。一旦定义了引用,就无法令其再绑定到另外的对象,之后每次使用这个引用都是访问它最初绑定的那个对象。 指针和它存放的地址之间就没有这种限制了。和其他任何变量(只要不是引用)一样,给指针赋值就是令它存放一个新的地址,从而指向一个新的对象: ```C++ 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 //.... ``` <mark>任何非0指针,对应的条件值都是true。</mark> ## void* 指针 void*是一种特殊的指针类型,可以用于存放<mark>任意对象</mark>的地址。 ```C++ double obj = 3.14, *pd = &obj; //正确:void*能存放任意类型对象的地址 void *pv = &obj; //obj可以是任意类型的对象 pv = pd; //pv可以存放任意类型的指针 ``` 利用void* 指针能做的事比较有限:拿它和别的指针比较、作为函数的输入或输出,或者赋值给另一个void* 指针。不能直接操作void*指针所指的对象,因为我们并知道这个对象到底是什么类型,也就无法确定能在这个对象上做哪些操作。 ## 指向指针的指针 通过* 的个数可以区分指针的级别。也就是说,**表示指向指针的指针,***表示指向指针的指针的指针,以此类推: ```C++ int ival = 1024; int *pi = &ival; //pi指向一个int型的数 int **ppi = π //ppi指向一个int型的指针 ``` 此处pi是指向int型数的指针,而ppi是指向int型指针的指针,下图描述了它们之间的关系。  解引用int型指针会得到一个int型的数,同样,解引用指向指针的指针会得到一个指针。此时为了访问最原始的那个对象,需要对指针的指针做两次解引用: ```C++ cout << "输出方式:\n" << "int类型输出:" << ival << "\n" << "指向ival的指针:" << *pi << "\n" << "指向指针的指针:" << **ppi << endl; ``` 该程序使用三种不同的方式输出了变量ival的值:第一种直接输出;第二种通过int型指针pi输出;第三种两次解引用ppi,取得ival的值。 ## 指向指针的引用 引用本不是一个对象,因此不能定义指向引用的指针。但指针是对象,所以存在对指针的引用: ```C++ 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 日 © 允许规范转载 打赏 赞赏作者 支付宝 赞 如果觉得我的文章对你有用,请随意赞赏
2 条评论
段落衔接自然,过渡流畅,读来一气呵成。
对话设计自然,符合角色身份与情境。