![cover](https://img-blog.csdnimg.cn/5c7414f7e37642f0936ef3cf7cf26ed5.png)
【学习笔记】指针常量和常量指针
对于 pointer to const 来说,显然指针指向的是一个常量(当然它也可以指向一个非常量),对于 const pointer 来说,显然它自己是一个常量,即这个指针是一个常量,它蕴含的意思是这个指针指向的地址永远不可以改变,就好像你永远住在 xx 省 xx 市 xx 路 xx 号一样。上面的代码中,cptr 本来是指向 pi,但是当 cptr 转而指向 e 的时候就会报错,因为 cptr
⚠️
本文之前有个图画错了,经 C 友指正,我重新完善了本文,感谢每一位参与讨论的 C 友。
本文在修改的过程中重点参考了《Difference between constant pointer, pointers to constant, and constant pointers to constants》这篇文章,也特别推荐给大家
指针常量
指针常量是指向常量的指针,即 pointer to const,即指针指向的是一个常量,你应该把这个词(指向常量的指针)当做一个整体来理解,而不是分开。(当然也有翻译成指针常量的,但我并不喜欢这种翻译方式)
通常也将指针本身是一个常量称为顶层 const,将指针所指的对象是个常量称为底层 const
关系总结如下
- 常量指针 = * const = 顶层 const
- 指向常量的指针 = const * = 底层 const
【注】一定是从右往左读
它的语法格式是 const 在 * 左边,这一点很重要,因为后面要讲的常量指针是 const 在 * 的右边。下面是它的基本语法格式
const double pi = 3.14; // pi 是一个常量
const double *cptr = π // cptr 是一个指向常量的指针
// 或者,它的另一种写法是交换一下 const 和 double 的位置顺序
double const *cptr = π // cptr 是一个指向常量的指针
它的含义是,不能修改指针指向的内容,比如这里 cptr 指向 pi,不能通过 cptr 修改 pi 的内容。
下面我们举一个例子来进行具体的讲解
#include <iostream>
using namespace std;
int main()
{
const double pi = 3.14; // pi 是一个常量
const double *cptr = π // cptr 是一个指向常量的指针
cout << *cptr << endl; // 3.14
return 0;
}
这里有一个常量 pi,我用 cptr 这个指向常量的指针来指向它,它的内存示意图应该如下
【注】当我说指针的值(内容)的时候,我说的是 0x456。当我说指针指向的值(内容),我说的是 3.14,下面的语境请随着具体上下文自动脑修。
关于指针常量的两个扩展:
第一个是我们用普通的指针不能指向这个常量
const double pi = 3.14; // pi 是一个常量
double *pnormal = π // 用普通指针指向常量 pi
// error: invalid conversion from ‘const double*’ to ‘double*’
return 0;
答案显然是不行的,报错提示这是一个非法类型转换。
事实上,有这样的一个指向关系,指向常量的指针可以指向常量和非常量,而普通指针只能指向非常量,如下图所示。
对上图,从逻辑上是这么理解的,假设一个普通指针 common_pointer
指向且只能指向非常量 non_const_var
,通过对 common_pointer
的解引用,我们可以修改非常量 non_const_var
的值。但是如果普通指针 common_pointer
指向一个常量 const_var
,理论上来说,可以对普通指针 common_pointer
解引用而修改常量 const_var
的值,但常量又不能修改,所以普通指针只能指向非常量。
对于常量指针的逻辑亦是如此。
那么第二个问题是,cptr 指针指向的内容可以修改吗?显然也是不能修改的,因为它此时指向的是一个常量
const double pi = 3.14; // pi 是一个常量
const double *cptr = π // cptr 是一个指向常量的指针
cout << *cptr << endl; // 3.14
*cptr = 9.8; // error: assignment of read-only location ‘* cptr’
return 0;
可以看到报错的提示是, cptr 具有只读属性。特别的是,即使 cptr 指向的是非常量,它也不能修改这个非常量的内容(如下代码),仅记住一点即可,那就是 cptr 自己本身具有的属性就是只读的。
double pi = 3.14; // pi 是一个变量
const double *cptr = π // cptr 是一个指向常量的指针
cout << *cptr << endl; // 3.14
*cptr = 9.8; // error: assignment of read-only location ‘* cptr’
虽然指针常量不能修改指向地址的值,但是它可以修改指向的地址,如下可以看到指针常量 a 先指向 b 的地址,之后有成功指向 c 的地址
#include <iostream>
using namespace std;
int main()
{
int b = 10;
int c = 20;
const int* a = &b;
cout << *a << endl; // 10
a = &c;
cout << *a << endl; // 20
}
在上图中,指向常量的指针还可以指向一个非常量,而当指向常量的指针指向非常量的时候,依旧,不能通过该指针修改这个非常量,如下。
int main()
{
int nonconst = 100;
int nonconst2 = 300;
const int* pointer2const = &nonconst;
cout << *pointer2const << endl; // 100
// *pointer2const = 200; // error
pointer2const = &nonconst2; // 可以指向其他非常量
cout << *pointer2const << endl; // 200
return 0;
}
常量指针
常量指针寓意着指针本身的内容不能被修改,即该指针被固定指向一个内存空间。它的语法是 const 在 * 的右边:char *const p = greeting;
你可以这么理解它的含义,很多时候,我们可以对指针进行递增(减)操作,来移动它在内存中的位置,常量指针就是指不能进行这样的移动。
虽然这幅图已经说明了一切,但我们还是不妨用代码来进一步解释
int main()
{
int a = 100;
int b = 400;
int* const ptr = &a;
cout << *ptr << endl;
cout << ptr << endl; // 假设 ptr 指向 a 的地址为 0x123
*ptr = 200; // 可以改变 0x123 中存的值,这里改为 200
cout << *ptr << endl; // 200
a = 300; // 可以改变 0x123 中存的值,这里改为 300
cout << *ptr << endl; // 300
// ptr = &b; // error,但不能再让 ptr 指向其他的地址了
return 0;
}
上面的代码中,常量指针 ptr
首先指向了 a
的地址,就好像 ptr
永远定居在了 a
这栋房子里。但是你可以改变 a
房子里的装修,比如,我先将 a
地址通过 *ptr
装修成了 200,又通过 a
将房子里装修成了 300。
但当我要将 ptr 迁居到 b 地址时,编译器告诉我,你已经离不开 a 了。
拓展
现在我们思考这样一个问题,存在以下代码,它能否通过编译呢?
int main()
{
char c = 'c';
char* pc;
pc = &c;
const char** pcc = &pc;
return 0;
}
事实是不能的,在 const char** pcc = &pc;
位置会报错 error: invalid conversion from ‘char**’ to ‘const char**’
。原因是,我们的二重指针是 const
的,当 pcc 通过 pc 指向 c 后,逻辑上,我们不希望通过 pcc 来改变 c,但是,pcc 在指向 pc 的时候,pc 不是 const
的,意味着 pc 可以随时改变指针的指向。
上面的代码将 pc 设置为 const
就可以正常运行了
int main()
{
char c = 'c';
const char* pc;
pc = &c;
const char** pcc = &pc;
c = 'a';
cout << c << endl; // a
return 0;
}
参考
- 《Primer C++》
- 《C++ 面向对象高效编程》
- 简述指针常量与常量指针的区别
更多推荐
所有评论(0)