在C++学习中,const 是一个非常基础但极易混淆的关键字,尤其是当它和指针结合时,很多新手都会被 const int *p、int *const p 这类写法搞懵。
其实不用慌,核心就一个原则:const 离谁近,谁就不能改。
一、先搞懂基础:const 修饰普通变量
这是最简单、最常用的用法,一句话就能说透:const 修饰普通变量,就是给变量"上锁",让它变成只读的,不能再修改。
代码示例:
cpp
const int a = 10; // 用const修饰变量a
a = 20; // 报错!不能修改const修饰的变量
核心特点:变量本身的值不可变,一旦初始化,就不能再赋值。这是 const 最基础的用法,没什么难度,重点在后面和指针的结合。
二、重点难点:const 修饰指针(3种情况)
const 和指针结合,之所以容易乱,是因为 const 可以修饰"指针本身",也可以修饰"指针指向的内容"。记住一个口诀,瞬间理清:
cpp
const 在 * 左边 → 指针指向的内容不能改
const 在 * 右边 → 指针本身不能改
两边都有 → 内容和指针都不能改
1. const 在 * 左边:指向的内容不能改(此时const 修饰*p,也就是地址里存的值不能改)
写法有两种,效果完全一样,随便写哪种都可以:
cpp
const int *p; // 写法1
int const *p; // 写法2
核心规则:不能通过指针 p 修改它指向的内容,但指针 p 本身可以指向别的变量。
代码示例:
cpp
int a = 10, b = 20;
const int *p = &a; // 指针p指向a
*p = 100; // 报错!不能修改指向的内容
p = &b; // 正确!指针可以换指向
2. const 在 * 右边:指针本身不能改(此时const修饰p,也就是地址不能改)
写法只有一种,注意 const 的位置
cpp
int *const p;
:指针 p 不能再指向别的变量,但可以修改它指向的内容:
cpp
int a = 10, b = 20;
int *const p = &a; // 指针p固定指向a(初始化时必须赋值,否则报错)
*p = 100; // 正确!可以修改指向的内容(电视可以调音量)
p = &b; // 报错!指针不能换指向(遥控器不能换台)
注意:这种写法,指针 p 必须在初始化时就指定指向的变量,不能先定义再赋值(比如 int *const p; 单独写会报错)
2.1 引用的底层实现 = const指针(int *const ref)
:即"指针本身不能改指向",这也是引用不能改绑的原因;
我们写一句简单的引用代码:
cpp
int a = 10;
int &ref = a; // 引用ref绑定变量a
这段代码,编译器在底层会悄悄翻译成类似这样的指针操作(注意:这是底层实现,我们写代码不能这么写):
cpp
int a = 10;
int *const ref = &a; // 底层用const指针实现引用
这就解释了两个关键问题:
-
为什么引用必须初始化? → 因为const指针(
int *const ref)必须在初始化时指定指向,引用底层是const指针,所以语法上要求引用必须绑定变量才能定义,不能先定义再赋值(比如int &ref;会报错)。 -
为什么引用不能改绑? → 因为底层的const指针,本身不能改指向(
ref = &b会报错),所以引用一旦绑定某个变量,就不能再绑定其他变量(比如ref = b是给a赋值,不是改绑)。
3. 两边都有 const:内容和指针都不能改
cpp
const int *const p;
核心规则:既不能修改指向的内容,也不能修改指针本身的指向,相当于"双重锁定"。
cpp
int a = 10, b = 20;
const int *const p = &a;
*p = 100; // 报错!不能改内容
p = &b; // 报错!不能改指向
三、一张表彻底记牢(建议收藏)
为了方便大家快速查阅,整理了所有情况的对比,一目了然:
| 写法 | 通俗含义 | 指向的内容(*p) | 指针本身(p) |
|---|---|---|---|
| int *p | 普通指针 | 可修改 | 可修改 |
| const int *p / int const *p | 指向常量的指针 | 不可修改 | 可修改 |
| int *const p | 指针本身是常量 | 可修改 | 不可修改 |
| const int *const p | 内容和指针都不可变 | 不可修改 | 不可修改 |