进一步了解const指针

问题

今天群里有人发了个问题,问题如下:

第一个赋值可以,为什么第二个不行

int * x = NULL;
int const * y = NULL;
y = x;

int ** z = 0;
int const ** n = 0;
n = z

当时我懵了一下,随即想到可能是int const **的问题,便把代码改成:

int ** z = 0;
int * const * n = 0;
n = z;

 编译通过,以为解决了问题,想也没想,便发回给提问者。 

殊不知,提问者问了好几个问题后,我又懵了,-_-!

这个const是属于那一边的?

可以是

int (* const) * n = 0;


那么n就是一个指向(int * const)的int指针


也可以是

int * (const *) n = 0;

那么n就是一个指向(int *)的const int 指针


后来提问者发了书里面的一句话:

如果const和(或)volatile关键字后面紧跟类型说明符(如int, long等),它作用于类型说明符,在其他情况下,const和(或)volatile关键字作用于它左边紧邻的指针星号。

本人觉得这句话不够确切,一般情况下,我们会把代码写成:

const int * p;

这和书中描述的一致,const 此时作用于int。但我们也有另外一种写法

int const * p;

虽然大家都知道这个const 必定作用于int的,但按照那句话的方法,const找不到作用的地方了。

好吧,这有点钻牛角了。既然是从书中出来的,还是得用代码来证明一下。


试验

回归到顶部问题,其实就是类型匹配问题,如果类型不匹配,那么编译就会报错,所以通过编译器(VS2010)来作为我们的裁判,const到底是作用于谁。

先铺好两个必须的变量:

int * p_to_v_1 = new int(1);;
int * p_to_v_2 = new int(2);

第一试验

int * * ppTest1 = &p_to_v_1;
ppTest1 = &p_to_v_2;
*ppTest1 = p_to_v_2;
**ppTest1 = 10;

这个应该理解上没有多大的问题,所以不解释。

编译通过,没报任何错误,结论:

*ppTest1也是int * 类型。

**ppTest1是int类型。


第二试验

int const * * ppTest2 = &p_to_v_1;
ppTest2 = &p_to_v_2;
*ppTest2 = p_to_v_2;
**ppTest2 = 10;

这个比较简单,ppTest2指向的是int const *的指针,所以第三个**ppTest2的赋值编译器报错:表达式必须是可修改的左值。

*ppTest2也是int const * (或const int *)类型。

**ppTest2是const int 类型,不可改变,所以赋值时出错。


第三试验

int * const * ppTest3 = &p_to_v_1;
ppTest3 = &p_to_v_2;
*ppTest3 = p_to_v_2;
**ppTest3 = 10;
const 来到两个星号中间,那么就是上面我遇到的问题了。

编译器此时报错是 *ppTest3 = p_to_v2; 这行代码,错误是:表达式必须是可修改的左值。

p_to_v2是指针变量,那么*ppTest3就是指针常量

*ppTest3是int * const类型。(印证了书中那句话)

**ppTest3那一行没报错,那么**ppTest3就是int类型


第四试验

int * * const ppTest4 = &p_to_v_1;
ppTest4 = &p_to_v_2;
*ppTest4 = p_to_v_2;
**ppTest4 = 10;
报错的是“ppTest4 = p_to_v2;”这行,错误信息和上面例子一样,那么ppTest4就是一个指向指针的指针常量,ppTest4是不可更改的。


第五试验

int const * const * ppTest5 = &p_to_v_1;
ppTest5 = &p_to_v_2;
*ppTest5 = p_to_v_2;
**ppTest5 = 10;
有了前面的基础,应该很容易判断这里报错的是哪个。

没错,第3行和第4行错误,*ppTest5是指针常量,**ppTest5是常量。


第六试验

int * const const * ppTest6 = &p_to_v_1;
ppTest6 = &p_to_v_2;
*ppTest6 = p_to_v_2;
**ppTest6 = 10;
这个定义看起来有点奇怪,虽然会有编译警告,但是可以通过的。

报错的只有第3行(*ppTest6 = p_to_v_2;),因为*ppTest6是指针常量。

为什么第2行不报错呢?ppTest6是指向指针常量的指针,&p_to_v_2的结果又是指向指针变量的指针。

因为这个赋值的道理就如const int * 类型可以被int *类型赋值一样(如函数void f(const int * cp)中的参数,f被调用时,传入的参数可以是const int*,也可以为int *),一个是指向int常量的指针,一个是指向int变量的指针。

这里的定义(int (* const) (const *) ppTest6 )还有个地方需要注意一下,刚才说了,ppTest6是指向指针常量的指针;而刚好,*ppTest6又是指针常量,那这个定义就相当于

int * const * ppTest6 

第七试验

int * const * const ppTest7 = &p_to_v_1;
ppTest7 = &p_to_v_2;
*ppTest7 = p_to_v_2;
**ppTest7 = 10;
2和3行报错,ppTest7是常量,*ppTest7也是。

第八试验

int const * const * const ppTest8 = &p_to_v_1;
ppTest8 = &p_to_v_2;
*ppTest8 = p_to_v_2;
**ppTest8 = 10;
这个2、3、4都报错,应该不用怎么解释了吧。。。


总结一下,刚才那句话大部分还是对的,就差了我刚才提到的那一点,嘿嘿。

如果const和(或)volatile关键字后面紧跟类型说明符(如int, long等),它作用于类型说明符,在其他情况下,const和(或)volatile关键字作用于它左边紧邻的指针星号。


解决问题

刚才说明了const在不同位置所起的不同作用,现在回过头来看原先的问题:

int * x = NULL;
int const * y = NULL;
y = x;

int ** z = 0;
int const ** n = 0;
n = z
现在来看这些指针,应该比较明确:

z就是指向int *的指针;

而n就是指向const int *的指针;

n和z最终指向类型(一个是int, 一个是const int)是不一样的,所以赋不了值。

就如以下代码

int * x = NULL;
int * const y = NULL;
y = x;

请不要跟第六试验混淆,第六个试验中,ppTest6和p_to_v_2最终指向的都是int类型。


在一般情况下,应该不会出现这种const和指针的指针混用的X疼情况,不过这样一来,自己也是对指针有更深一层了解,指针确实是C和C++中最难攻克的一关,希望通过这能帮助到其他人,如果有不足,请不吝赐教。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值