const和一二级指针的结合应用
一、const 修饰的量叫常量,它和普通变量的区别是什么?
C++有两点:
1、编译方式不同,用立即数替换
2、不能作为左值被修改
二、const修饰的量(即常量)常出现的错误是:
1、常量不能再作为左值(即不能直接修改常量的值)
2、不能把常量的地址(&a)泄漏给一个普通的指针(p)或者普通的引用变量(可以间接修改常量的值)
int main() {
const int a = 10;
int *p = &a; ❌ // *p = 30; int * <= const int *
// *就相当于地址,因为地址的学名是指针 ,两者互相等价,所以&a的数据类型就是const int *
// 两边的数据类型不等,故不能执行,需要把&a强转成(int *)或者左边的p的数据类型改成const int *
// 为什么要把左边的类型改成const int *呢?是因为改了之后,一方面是两边类型一致, 一方面是const int *p的话,就是表明*p是不能够被修改的,这样就符合const int a的意思,不修改a的值,这样a才能够放心地把它的地址告诉p,因为反正*p也改不了a的值
}
三、const和一级指针的结合 有两种情况:
①不能修改指针的解引用
②不能修改指针的指向
C++的语言规范:const修饰的是离它最近的类型(有点像飞轮海的《只对你有感觉》,const就只对离它最近的数据类型有感觉)
const int *p; // 这里const修饰的类型是int,不是int*, 因为int自己就可以作为一个类型。那其实我们更在意的是const修饰的是什么表达式,除去int这个数据类型,剩下的就是const修饰的表达式了, 就是*p。也就是说*p不能再被赋值了,故你可以通过指针来指向某块内存,但是你不能够通过指针解引用(*p)来修改这块内存里的值。
// *p = 20❌ p = &b ✅
// 因为指针p本身没有任何东西修饰它,const修饰的是*p这个表达式,而不是p,所以p可以被重新赋值
// 综上,p可以任意指向不同的int类型的内存,但是不能通过指针间接修改指向的内存的值
int const *p; //这里*不能够独立作为一个数据类型,所以const修饰的最近的数据类型是int,const修饰的表达式是*p,这样的话就跟上面那个一样了,就是const的位置改变了一下而已。我们经常把写成上面那种形式
int *const p;
// p = &b;❌ *p = 20;✅
// 这个指针p现在是个常量,不能再指向其它内存,但是可以通过指针解引用修改指向的内存的值
const int * const p; //这个就相当于是上面的结合
// 第一个const修饰的表达式是*p,第二个const修饰的表达式是p
// 这样就显得非常严格,既不让*p被修改,也不让p被修改
const如果右边没有指针*的话,const是不参与类型的
四、总结const和指针的类型转换公式:
① int * <= const int * ❌ 因为如果把右边常量赋值给左边的普通变量,那左边一解引用就可以对常量的值进行修改了,这样是不被允许的。
② const int * <= int * ✅ 普通变量赋到左边常量去倒是没有什么约束的。
③ int ** <= const int ** ❌
④ const int ** <= int ** ❌ const和多级指针结合的时候,赋值时必须两边都有const (有空可以思考一下为什么)
⑤ int ** <= int * const* ❌ // 这是const和一级指针结合的情况,因为只看const后面的星号*,前面的 * 和 const没关系。两边都去掉 int *,那么就成了 * <= const * ,就跟第一个一样了,所以是错误的。
⑥ int * const* <= int ** ✅ // 跟⑤类似,去掉int * 看一下就知道了,去掉之后就是②了,所以是正确的。
int a = 10;
const int *p = &a; // 不管a是普通变量还是常量,对于编译器来说,它永远认为p里面放的是整型常量的地址
int *q = p; // int * <= const int *❌ 不能把整型常量的地址赋给一个普通的指针
// 可能会有面试官这么绕你,说p存放a的地址,p赋值给q,q存放a的地址,q是普通变量,那么可以通过*q修改a的值。那么这样做有什么错误吗??❌ 这样说是错误的,就算这里没a,也是错的。
// 也可以是const int *p = nullptr;
// 这样的话就直接看出来是int* <= const int * ❌
// 不管a是普通变量还是常量,对于编译器来说,它永远认为p里面放的是整型常量的地址
int *q1 = nullptr; // NULL是宏名,可以赋值给空指针或整型,容易跟整数混用,所以不好,C++11以后专门给指针一个初值nullptr,这样就不会和整数混用了。
int *const q2 = nullptr;
cout << typeid(q1).name() << endl;
cout << typeid(q2).name() << endl;
// 输出的结果都是int *
// 结论:const如果右边没有指针*的话,const是不参与类型的
// 所以q2的数据类型是int *,const则表示q2本身是个常量,表示它不能作为左值,不能将它的地址泄漏给一个普通的二级指针。
int a = 10;
int *p1 = &a;
const int *p2 = &a; // const int * <= int *
int *const p3 = &a; // 综合上面的结论:这里就是int* <= int*
int *p4 = p3; // 实际上也是int * <= int *
五、const和二级指针的结合
int main() {
int a = 10;
int *p = &a;
const int **q = &p; ❌ // const int** <= int **
// 跟多级指针结合应用,必须两边都有const
/*
const int **q = &p;这一句话错误的原因如下:
*q <=> p
*q访问的是一级指针的内存,p是一级指针的内存,都是一级指针的内存
const int b = 20;
*q = &b; // 就相当于p = &b,这样就违背了前面的一个结论——不能把常量的地址泄漏给一个普通的指针
// &b相当于一个一级指针
*/
/*
有两种修改方式:
① const int *p= &a;
(在第二句话钱加个const,这样就不用担心上面说的那种将常量地址泄漏给普通指针的情况了)
② const int * const* q = &p;
(第三句话中间加个const,前面说出现错误的原因就是因为把&b赋值给*q,那我们可以不让*q被赋值,限定它为一个常量,不能作为左值被修改,故最后就不可能通过*q = &b这种方式,把一个常量的地址泄漏给一个普通的指针,这样就不会报错了)
*/
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具