const 和 * 谁前谁后,锁的东西完全不同:const int *p 锁的是值(不能通过 p 改 a,但 p 能改指别人),叫"指向常量的指针";int * const p 锁的是指针(p 不能再指别人,但能改 a 的值),叫"指针常量";两个都写 const int * const p,两样都锁。口诀:const 贴着谁,就锁谁。
7.4.4 讲了第一根安全带(空指针)。这一节是第二根:用 const 限制指针的权限。它用的零件是 2.2.5 学过的 const(只读常量)。这一节的读法一旦掌握,后面 7.6 的函数参数(常写成 const char *s)和 7.8 的引用(const int &r)就都看懂了一半。
一、三兄弟排排站
假设有 int a = 10;,三种写法的权限完全不同:
const int *p1 = &a; // ① 指向常量的指针
int * const p2 = &a; // ② 指针常量
const int * const p3 = &a; // ③ 两个都锁
| 写法 | 名字 | 能不能改 值(*p = 20) |
能不能改 指针(p = &b) |
|---|---|---|---|
const int *p1 |
指向常量的指针 | ❌ 不能 | ✅ 能 |
int * const p2 |
指针常量 | ✅ 能 | ❌ 不能 |
const int * const p3 |
两者都是常量 | ❌ 不能 | ❌ 不能 |
注意名字和权限是"反着"的,这是最容易记混的地方:
- "指向常量的指针"——重点在"指向的那个东西是常量"→ 锁值;
- "指针常量"——重点在"这个指针自己是常量"→ 锁指针。
二、口诀:const 贴着谁,就锁谁
不用死记名字,看 const 挨着谁:
const int *p; // const 贴着 int → 锁"int"(值):*p 不能改
int const *p; // 同上,完全等价
int * const p; // const 贴着 p → 锁"p"(指针):p 不能改
const int * const p; // 两边都贴 → 两样都锁
一句话口诀:const 在 * 的左边 → 锁"值";在 * 的右边 → 锁"指针"。
记忆画面:* 像一堵墙,const 站哪边,哪边就被封住。
const int * p int * const p
┌─────┐ │ ┌─┐ ┌─┐ │ ┌─────────┐
│const│ * │p│ │ │ * │ const │
└─────┘ │ └─┘ └─┘ │ └─────────┘
锁"值" ┘ └─ 锁"指针"
三、int const *p 和 const int *p 是一回事
const int *p1; // ✅
int const *p2; // ✅ 和上面完全等价
因为 const 修饰的是它左边最近的东西(如果左边没有,就看右边)——const int 和 int const 都是"常整数",所以两个声明意思一样。
但下面这个完全不是一回事:
const int *p; // 锁值
int * const p; // 锁指针
只有一个字的差别,含义天壤之别——这是本节最需要动手验证的地方。
四、动手 Demo:三种写法 + 现场报错
#include <iostream>
using namespace std;
int main() {
int a = 10, b = 20;
// ① 指向常量的指针:锁值,指针能改
const int *p1 = &a;
cout << "*p1 = " << *p1 << endl; // 10
// *p1 = 99; // ❌ 编译错误:不能改指向的值
p1 = &b; // ✅ 指针可以改指别人
cout << "改指 b 后 *p1 = " << *p1 << endl; // 20
// ② 指针常量:锁指针,值能改
int * const p2 = &a;
*p2 = 99; // ✅ 能改值
cout << "改值后 a = " << a << endl; // 99
// p2 = &b; // ❌ 编译错误:指针不能再指别人
// ③ 两个都锁
const int * const p3 = &b;
// *p3 = 1; // ❌ 值不能改
// p3 = &a; // ❌ 指针也不能改
cout << "*p3 = " << *p3 << endl; // 20
return 0;
}
看到什么算成功:*p1 = 10、改指 b 后 *p1 = 20、改值后 a = 99、*p3 = 20。
一定要亲手做的事:把三条注释掉的语句依次解除注释,每次重新编译,看编译器报什么错:
| 解除注释 | 编译器会说什么 | 含义 |
|---|---|---|
*p1 = 99; |
assignment of read-only location '*p1' |
值被锁 |
p2 = &b; |
assignment of read-only variable 'p2' |
指针被锁 |
两条报错信息把本节的规则翻译成了大白话:read-only location 说的是"那块内存只读"(锁值);read-only variable 说的是"这个变量只读"(锁指针)。以后看到这两句话,就知道是哪种 const 拦住了你。
五、为什么需要它们
| 场景 | 用哪种 | 好处 |
|---|---|---|
| 函数只读传进来的字符串/数组,不改内容 | const char *s |
承诺"我不改你的数据",调用方放心,也能接收常量实参 |
| 把某个指针钉死(比如硬件寄存器地址) | int * const p |
防止程序中途把它指歪 |
| 常量表 + 固定位置 | const int * const p |
双重保险 |
| 给只读数据加一层访问 | const int * |
语法上禁止误写,编译器帮你把关 |
一句话:const 用在指针上,是把"不改"这件事写进类型里,让编译器替你把关——比在注释里写"请勿修改"可靠得多。
六、常见误区
- "
const int *p是 p 不能改"——错。是"p 指向的值不能改",p 自己能改指别人。想锁 p 要写int * const p。 - "
int * const p是值不能改"——反了。是指针不能改,值能改。 - "
int const *p和int * const p一样"——完全不同。前者等价于const int *p(锁值),后者锁指针。只差const的位置。 - "
const int * const p是多余"——不多余。它同时锁住两头,常用于"指向一个常量表且位置固定"的场景。 - "锁了值就没法改了"——通过这个指针没法改,但原变量还能直接改:
a = 5;依然合法(只要 a 自己不是const)。const int *p只是限制了"这条路"。
自测三问(点开对答案)
1. `const int *p` 和 `int * const p` 各锁什么?——前者锁**值**(`*p` 不可改),后者锁**指针**(`p` 不可改)。 2. `int const *p` 等价于哪一个?——`const int *p`(都锁值)。 3. 想让"指针和值都不能改",怎么写?——`const int * const p = &a;`- ☐ 7.5 指针与数组(下一节)
相关笔记
- 变量与指针 —— 指针的基础写法
- 指向空的指针与空类型指针 —— 第一根安全带
- 其他常量 ——
const的来历 - 🔷 C++基础 · 索引 —— 全课路线图