Constant Pointers and Pointers to Constants 展开介绍一下C++里这两个概念,用途,与用法,举例说明
好的,我们来深入讲解 C++ 中的 Constant Pointers(常量指针) 和 Pointers to Constants(指向常量的指针)。这两个概念经常容易混淆,但它们有着不同的语义和用途。我们从定义、语法、用法和示例逐步展开。
1️⃣ 指向常量的指针(Pointer to Constant)
定义:指针本身可以改变,但它指向的内容不可通过该指针修改。
语法:
const int* ptr;
int const* ptr; // 等价写法
特点:
*ptr是常量,不允许修改。ptr可以指向别的变量(即指针本身可以改变)。
用途:
- 当你希望函数或程序通过指针访问数据,但不允许修改数据时。
- 常用于函数参数传递,保护数据不被修改。
示例:
#include <iostream>
using namespace std;
int main() {
int a = 10;
int b = 20;
const int* ptr = &a; // 指向常量的指针
cout << *ptr << endl; // 10
// *ptr = 15; // 错误!不能通过ptr修改a
ptr = &b; // 指针本身可以改变
cout << *ptr << endl; // 20
return 0;
}
总结:
- 可以修改指针本身。
- 不可以通过指针修改它指向的值。
2️⃣ 常量指针(Constant Pointer)
定义:指针本身是常量,初始化后必须指向固定地址,但可以通过它修改指向的值(如果值本身不是 const)。
语法:
int* const ptr = &a;
特点:
ptr的值(即地址)不可改变。- 可以通过
*ptr修改所指向的内容(除非内容本身是 const)。
用途:
- 当你需要指针始终指向同一块内存,但允许修改数据时。
- 常用于需要稳定地址的场景,例如底层硬件寄存器、数组操作等。
示例:
#include <iostream>
using namespace std;
int main() {
int a = 10;
int b = 20;
int* const ptr = &a; // 常量指针
cout << *ptr << endl; // 10
*ptr = 15; // 可以修改 a
cout << a << endl; // 15
// ptr = &b; // 错误!不能改变 ptr 指向
return 0;
}
总结:
- 指针自身不可改变。
- 可以修改指针指向的内容。
3️⃣ 两者结合:指向常量的常量指针(const pointer to const)
有时你希望指针 既不能改变指向,也不能修改内容。
语法:
const int* const ptr = &a;
示例:
#include <iostream>
using namespace std;
int main() {
int a = 10;
const int* const ptr = &a;
cout << *ptr << endl; // 10
// *ptr = 20; // 错误,不能修改值
// ptr = &b; // 错误,不能改变指向
return 0;
}
用途:
- 安全的只读访问。
- 避免指针意外修改或指向错误地址。
4️⃣ 总结对比表
| 类型 | 指针本身是否可变 | 指针指向内容是否可变 | 典型语法 |
|---|---|---|---|
| Pointer to Const(指向常量) | 可变 | 不可变 | const int* ptr |
| Const Pointer(常量指针) | 不可变 | 可变 | int* const ptr |
| Const Pointer to Const(指向常量的常量指针) | 不可变 | 不可变 | const int* const ptr |
5️⃣ 应用场景总结
- 函数参数保护数据:
void printValue(const int* ptr) {
cout << *ptr << endl; // 不能修改值
}
- 固定指向某块内存,但允许修改内容:
int* const hardwareReg = ®Value;
*hardwareReg = 0xFF; // 写入寄存器
- 完全只读指针:
const int* const config = &SYSTEM_CONFIG;
如果你愿意,我可以帮你画一张 指针类型示意图,用箭头直观表示「指针能否改地址」和「能否改内容」,一眼就能分清这三类指针。
你希望我画吗? 好的,我们继续,把这三个指针类型用 图示化 的方式理解,更直观地看到「指针本身能否改地址」和「指针指向的内容能否修改」。
6️⃣ 指针类型图示
假设有一个变量 a = 10,用箭头表示指针指向:
① 指向常量的指针(Pointer to Const)
ptr
|
v
+-----+
| 10 | a
+-----+
*ptr不可改 → 内容上锁(const)。ptr可以改 → 箭头可以指向别的地址。
const int* ptr = &a;
int b = 20;
ptr = &b; // ✅ 可以改变指针指向
// *ptr = 30; // ❌ 锁住内容,不能通过 ptr 修改值
② 常量指针(Const Pointer)
ptr (固定)
|
v
+-----+
| 10 | a
+-----+
ptr不可改 → 箭头固定。*ptr可改 → 可以修改 a 的值。
int* const ptr = &a;
*ptr = 30; // ✅ 可以修改值
// ptr = &b; // ❌ 指针固定,不能指向别的变量
③ 指向常量的常量指针(Const Pointer to Const)
ptr (固定)
|
v
+-----+
| 10 | a (只读)
+-----+
ptr不可改 → 箭头固定。*ptr不可改 → 内容锁住。- 完全只读安全。
const int* const ptr = &a;
// *ptr = 30; // ❌ 内容锁住
// ptr = &b; // ❌ 指针固定
7️⃣ 对比记忆小技巧
可以用「箭头 + 锁」来记忆:
| 类型 | 箭头能否移动 | 内容能否修改 | 记忆方法 |
|---|---|---|---|
| Pointer to Const | ✅ | ❌ | 内容锁住,箭头自由 |
| Const Pointer | ❌ | ✅ | 箭头固定,内容自由 |
| Const Pointer to Const | ❌ | ❌ | 内容锁 + 箭头锁 |
口诀:
指向常量 → 内容锁;常量指针 → 箭头锁;两者都加 → 双锁。
8️⃣ 实际用途示例
函数参数安全
void display(const int* arr, int n) {
for(int i=0; i<n; i++)
cout << arr[i] << " "; // ✅ 可以读取
// arr[0] = 100; // ❌ 不能修改
}
固定指针操作硬件
int reg = 0;
int* const regPtr = ®
*regPtr = 0xFF; // 写入寄存器
只读配置
const int SYSTEM_CONFIG = 42;
const int* const cfgPtr = &SYSTEM_CONFIG;
// 完全只读,安全
如果你希望,我可以帮你做一张 彩色一页图表,把三类指针的 语法、箭头示意、可变性、应用场景 全部整合在一张图里,一眼就能理解。
你希望我做吗? 好的,我们继续,把前面的概念再往 更实战和易混淆的用法 扩展,帮助你真正理解并应用。
9️⃣ 指针 const 的组合与函数参数
在 C++ 中,指针的 const 可以出现在两个位置:
- 指针本身是常量 →
int* const ptr - 指向的数据是常量 →
const int* ptr - 同时都是常量 →
const int* const ptr
函数参数示例
(a) 指向常量的指针
void foo(const int* ptr) {
// 不能修改 *ptr
cout << *ptr << endl;
}
- 可传入
int*或const int*。 - 用于保护数据,避免函数内部修改原变量。
(b) 常量指针
void bar(int* const ptr) {
// 可以修改 *ptr
*ptr += 1;
// ptr = nullptr; // ❌ 不允许修改指针本身
}
- 通常用于内部必须固定指针,保证指针始终指向同一个对象。
(c) 指向常量的常量指针
void baz(const int* const ptr) {
cout << *ptr << endl;
// *ptr = 10; // ❌ 不允许修改
// ptr = nullptr; // ❌ 不允许修改指针
}
- 用于 只读且指针不可变 的安全场景。
🔹 10️⃣ 高级用法:数组与指针 const
数组参数
void printArray(const int* arr, int n) {
for(int i=0;i<n;i++)
cout << arr[i] << " "; // ✅ 可以访问
// arr[0] = 100; // ❌ 不允许修改
}
常用于函数保护数组不被修改。
常量指针数组
int a=1, b=2, c=3;
int* const ptrs[3] = { &a, &b, &c };
// ptrs[i] 不可以修改(指针固定),但 *ptrs[i] 可以修改
*ptrs[0] = 10; // ✅
指向常量的指针数组
const int x=5, y=6, z=7;
const int* arr[3] = { &x, &y, &z };
// arr[i] 可以修改,指向别的变量
// *arr[i] 不可以修改
🔹 11️⃣ 常见易混淆点
int const* p和const int* p是一样的const int* p; // 指向常量的指针 int const* q; // 同样是指向常量int* const p和const int* const p的区别
int* const p→ 指针固定,但值可变const int* const p→ 指针固定,值也不可变
- 记忆技巧:
看
*左右:*左边是类型 → 数据 const*右边是类型 → 指针 const
🔹 12️⃣ 小结
| 类型 | 指针本身是否可变 | 指针指向内容是否可变 | 典型语法 | 用途 |
|---|---|---|---|---|
| Pointer to Const | ✅ | ❌ | const int* ptr |
保护数据,不允许修改 |
| Const Pointer | ❌ | ✅ | int* const ptr |
固定指向,允许修改内容 |
| Const Pointer to Const | ❌ | ❌ | const int* const ptr |
安全只读,指针和内容都锁定 |
口诀复习: 内容锁 →
const在类型左边或左边的类型上 箭头锁 →const在*右边或末尾
如果你需要,我可以帮你画一张 C++ 指针 const 类型全图, 包含三类指针、箭头示意、数组、函数参数和可变性标记,像“思维导图”,一眼就能分清。
你希望我画吗? 好,我们接着把概念升级到 更实用和高级的场景,特别是在 函数参数、类成员、数组与引用中 的应用,让你在实际 C++ 开发中快速分辨使用哪种指针类型。
13️⃣ 函数参数中的使用
(1) 保护数据不被修改
void printValue(const int* ptr) {
cout << *ptr << endl;
// *ptr = 10; // ❌ 不允许修改
}
- 传入指针,不改变指针指向。
- 指向数据不可改。
(2) 固定指针操作特定对象
void increment(int* const ptr) {
*ptr += 1; // ✅ 可以修改值
// ptr = nullptr; // ❌ 指针固定
}
- 保证函数内部指针不会指向其他对象。
- 数据可以修改。
(3) 只读安全
void readOnly(const int* const ptr) {
cout << *ptr << endl;
// *ptr = 20; // ❌ 不可修改
// ptr = nullptr; // ❌ 不可修改
}
- 指针和内容都不可改,适合只读配置或硬件寄存器。
14️⃣ 类成员中的使用
指针成员变量可以是 const,也可以指向 const 数据。
class Device {
int* const reg; // 指针固定,指向可变数据
const int* config; // 指针可变,指向只读数据
const int* const sysCfg; // 指针固定,数据不可变
public:
Device(int* r, const int* c, const int* s) : reg(r), config(c), sysCfg(s) {}
void writeReg(int val) { *reg = val; } // ✅ 修改寄存器
void setConfig(const int* newCfg) { config = newCfg; } // ✅ 可以改变指向
void showSysCfg() { cout << *sysCfg << endl; } // ✅ 只读
};
int* const reg→ 保证指针 reg 总是指向同一个寄存器。const int* config→ 可以修改 config 指向其他配置,但不能修改值。const int* const sysCfg→ 指针和内容都固定,只读安全。
15️⃣ 数组与指针 const
- 指向数组元素的指针
const int arr[] = {1,2,3};
const int* p = arr; // 指向常量
// *p = 10; // ❌ 不可修改
p++; // ✅ 可以移动指针
- 常量指针指向数组元素
int arr2[] = {4,5,6};
int* const q = arr2; // 指针固定
*q = 10; // ✅ 可以修改第一个元素
// q++; // ❌ 指针固定
- 指向常量的常量指针数组
const int a=1, b=2, c=3;
const int* const arrPtr[3] = { &a, &b, &c };
// arrPtr[0] = &b; // ❌ 指针固定
// *arrPtr[0] = 10; // ❌ 内容不可改
16️⃣ 高级混合使用技巧
函数返回 const 指针
const int* getConfig() {
static int cfg = 100;
return &cfg; // 返回只读数据
}
指针常量引用
int x = 10;
int* ptr = &x;
int* const &ref = ptr; // 引用一个常量指针
*ref = 20; // ✅ 修改 x
// ref = nullptr; // ❌ ❌ 引用固定,不能改变 ptr
- 高级场景:保证函数或类成员不改变外部指针变量本身,但允许修改指向内容。
17️⃣ 易混淆总结
| 类型 | 指针本身可变? | 指向内容可变? | 场景 |
|---|---|---|---|
const int* p |
✅ | ❌ | 函数参数保护数据 |
int* const p |
❌ | ✅ | 固定寄存器或类成员 |
const int* const p |
❌ | ❌ | 系统配置、只读硬件寄存器 |
int* const & r |
❌ | ✅ | 高级引用保证指针不被重置 |
const int* & r |
✅ | ❌ | 函数参数安全,指针可重置指向 |
如果你愿意,我可以帮你画一张 彩色思维导图,把 指针类型、箭头示意、数组、函数参数、类成员、高级引用 全部整合,成为一张 C++ 指针 const 全景图,看一眼就能理解所有组合。
你希望我画吗? 好的,我们进一步升级,把 指针 const 的概念延伸到现代 C++ 中常见的实战场景,包括 智能指针、函数返回值、STL 容器、模板,这样你不仅能掌握语法,还能理解实际用途。
18️⃣ 指针 const 在智能指针中的应用
在现代 C++ 中,我们很少直接使用裸指针管理资源,而是用智能指针:
(1) shared_ptr<const T>
- 智能指针本身可变,但管理的对象不可修改。
#include <memory>
#include <iostream>
using namespace std;
int main() {
auto sp = make_shared<const int>(42);
// *sp = 10; // ❌ 不可修改
auto sp2 = sp; // ✅ 智能指针可以复制
cout << *sp2 << endl; // 42
}
(2) const shared_ptr<T>
- 智能指针本身不可修改(指向固定对象),但对象可修改。
auto sp = make_shared<int>(10);
const shared_ptr<int> csp = sp;
// csp = sp2; // ❌ 不可改变指针本身
*csp = 20; // ✅ 对象可修改
(3) const shared_ptr<const T>
- 完全只读:指针固定,内容不可改。
19️⃣ 函数返回 const 指针/引用
返回 const 指针/引用,避免调用者修改内部数据:
class Config {
int value = 100;
public:
const int* getValuePtr() const { return &value; } // 返回只读指针
const int& getValueRef() const { return value; } // 返回只读引用
};
getValuePtr()返回只读指针getValueRef()返回只读引用- 防止外部直接修改类内部数据,提高封装性。
20️⃣ STL 容器与 const 指针
在 STL 容器中,const 指针常用来保护容器元素不被修改:
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> v = {1,2,3};
for (const int* p = &v[0]; p != &v[0]+v.size(); ++p) {
cout << *p << " "; // ✅ 只读访问
// *p = 10; // ❌ 不可修改
}
}
或者通过迭代器访问:
vector<int> v = {1,2,3};
for (auto it = v.cbegin(); it != v.cend(); ++it) {
// *it = 10; // ❌ 不可修改
cout << *it << " ";
}
cbegin()/cend()返回 const_iterator,相当于const T*。
21️⃣ 模板与 const 指针
在模板中使用 const 指针可以增强安全性:
template<typename T>
void printArray(const T* arr, size_t n) {
for(size_t i=0;i<n;i++)
cout << arr[i] << " "; // 只读访问
}
- 可以接收各种类型数组,且不允许函数修改数组元素。
- 防止模板函数破坏数据安全。
22️⃣ 实战总结:什么时候用哪种?
| 场景 | 建议指针类型 | 说明 |
|---|---|---|
| 只读函数参数 | const T* 或 const T& |
防止函数修改原数据 |
| 固定指向对象(如寄存器) | T* const |
保证指针地址不可改 |
| 完全只读对象 | const T* const |
指针和对象都不可修改 |
| STL 迭代访问 | const_iterator |
对象只读,迭代器可移动 |
| 智能指针管理 | shared_ptr<const T> 或 const shared_ptr<T> |
保证资源安全和不可修改性 |
23️⃣ 高级提示
- 优先用
const修饰数据而非指针
例如
const int* ptr,比int* const ptr在函数参数中更常用。- 在接口中尽量返回 const 引用或指针
提升封装性,避免调用者误修改内部数据。
- 智能指针结合 const
shared_ptr<const T>与unique_ptr<const T>都是现代 C++ 推荐的安全写法。
如果你愿意,我可以帮你画一张 C++ 指针 const 全景图, 内容包括:
- 三类裸指针(Pointer to Const / Const Pointer / Const Pointer to Const)
- 数组、函数参数、类成员示意
- 智能指针、STL 容器、模板使用
- 箭头 + 锁标记可视化
这样一张图就能把整个知识体系串起来,一眼就看懂。
你希望我画吗? 好的,我们继续,把 指针 const 的知识体系完全串起来,并延伸到 实际编程中容易出错和容易混淆的高级场景,帮助你在大型项目中正确使用。
24️⃣ 指针 const 的组合记忆法
在大型项目中,你会遇到各种组合,记忆上容易混淆:
| 类型 | 指针本身可变? | 指向内容可变? | 口诀 | 示例 |
|---|---|---|---|---|
| Pointer to Const | ✅ | ❌ | 内容锁,箭头自由 | const int* p |
| Const Pointer | ❌ | ✅ | 箭头锁,内容自由 | int* const p |
| Const Pointer to Const | ❌ | ❌ | 双锁 | const int* const p |
| Pointer to Const Pointer | ✅ | ❌ | 箭头自由,指向的指针锁 | int* const* p |
| Const Pointer to Const Pointer | ❌ | ❌ | 双重锁 | int* const* const p |
口诀方法:
- 看
*左边 → 数据 const- 看
*右边或末尾 → 指针 const- 双锁 → 数据锁 + 箭头锁
25️⃣ 多级指针与 const
多级指针中,const 可以加在多个层级:
int a = 10;
int b = 20;
// 指向常量的指针
const int* p1 = &a; // *p1不可改
const int* const p2 = &a; // *p2不可改, p2不可改
// 指向指针的常量
int* ptr = &a;
int* const* pp = &ptr; // pp可以移动,*pp指针固定
注意事项
const int** p与int* const* p完全不同:const int** p→ 可以通过*p修改指针指向的值,但该值是 const,可能导致安全问题。int* const* p→ 指针不可改,但可以修改内容。
⚠️ 多级指针 const 非常容易混淆,原则:
尽量少用多级裸指针,现代 C++ 用引用或智能指针替代。
26️⃣ 类成员中 const 指针
类内部固定资源管理
class Device {
int* const reg; // 常量指针,指针固定,可修改寄存器值
const int* config; // 指向常量,可修改指向
const int* const sysCfg; // 指针和内容都固定
public:
Device(int* r, const int* c, const int* s) : reg(r), config(c), sysCfg(s) {}
};
- 固定指针 → 保证成员始终指向正确资源
- 指向 const → 防止修改系统配置
- 指针 + 内容 const → 完全只读安全
27️⃣ 函数模板与 const
模板函数中使用 const 指针提升安全性:
template<typename T>
void printArray(const T* arr, size_t n) {
for(size_t i=0;i<n;i++)
cout << arr[i] << " ";
}
- 支持各种类型数组
- 防止函数修改原数组
- 可搭配
constexpr或std::array进一步优化
28️⃣ STL 与 const 指针
const_iterator实质上是 指向常量的指针vector<int>::const_iterator→const int*cbegin()/cend()返回 const_iterator,防止修改容器元素- 智能指针 + const → 安全管理资源,防止误修改
std::vector<int> v = {1,2,3};
for(auto it = v.cbegin(); it != v.cend(); ++it) {
// *it = 10; // ❌ 锁住数据
cout << *it << " ";
}
29️⃣ 高级实战建议
- 函数参数 → 尽量使用
const T*或const T&
避免函数内部修改外部数据
- 类成员 → 对指针成员使用
T* const或const T* const
- 类成员 → 对指针成员使用
提高封装性和安全性
- 数组与 STL 容器 → 使用
const迭代器或指针
- 数组与 STL 容器 → 使用
提供只读访问
- 多级指针 → 尽量用智能指针或引用替代裸指针
避免混淆和安全问题
- 智能指针 →
shared_ptr<const T>或const shared_ptr<T>
- 智能指针 →
保证资源安全和不可修改性
如果你愿意,我可以帮你做一张 最终全景图,把 裸指针 const、类成员 const、数组、函数参数、STL、智能指针、模板 全部整合成一张彩色可视化图, 箭头 + 锁标记直观显示每种 const 的含义和可修改性。
这样看一眼就能理解整个 C++ 指针 const 知识体系。
你希望我画吗? 好的,我们进入 C++ 指针 const 的终极整合与实战思路,帮助你从概念理解到实际工程应用彻底掌握。
30️⃣ 指针 const 的思维模型
我们可以用一个 “箭头 + 锁”模型 来理解:
- 箭头(Pointer)锁 → 指针本身能否改变指向
- 内容(Target)锁 → 指针指向的内容能否修改
| 类型 | 箭头能动? | 内容能改? | 场景 |
|---|---|---|---|
T* ptr |
✅ | ✅ | 普通指针,最灵活 |
const T* ptr |
✅ | ❌ | 指向只读数据,函数参数安全 |
T* const ptr |
❌ | ✅ | 固定指针,修改对象 |
const T* const ptr |
❌ | ❌ | 指针+内容都锁住,安全只读 |
口诀记忆
- 内容锁 → const 在类型左边
- 箭头锁 → const 在类型右边或末尾
- 双锁 → 双重 const
31️⃣ 多级指针 const 的原则
int a = 1;
int b = 2;
int* p = &a;
int* const* pp = &p; // 指针固定,但 pp 本身可动
const int** cpp = &p; // 指向 const 的指针(注意安全问题)
原则:
- 尽量少用多级裸指针
- 使用智能指针(
shared_ptr、unique_ptr)或引用替代 - const 用于保护数据和指针本身
- 多级 const 时,先分析每一级指向的对象是否可以修改
32️⃣ 函数参数应用总结
| 参数类型 | 用途 |
|---|---|
const T* ptr |
保护数据,指针可指向其他对象 |
T* const ptr |
指针固定,内容可修改(少见于函数参数) |
const T* const ptr |
只读、安全,适合配置或硬件寄存器 |
T* const & ptr |
高级引用,保证外部指针不可改,但允许修改对象 |
const T* & ptr |
引用可改指针,内容不可改 |
33️⃣ 类成员中的使用
class Device {
int* const reg; // 指针固定,内容可改
const int* config; // 指向常量,指针可改
const int* const sysCfg; // 指针+内容不可改
public:
Device(int* r, const int* c, const int* s) : reg(r), config(c), sysCfg(s) {}
void updateReg(int val) { *reg = val; }
void changeConfig(const int* newCfg) { config = newCfg; }
void readSysCfg() { cout << *sysCfg << endl; }
};
- reg → 固定硬件寄存器地址,允许写寄存器
- config → 配置可切换,但不能修改内容
- sysCfg → 系统配置只读,安全
34️⃣ STL 和智能指针结合 const
- STL 容器迭代访问:
std::vector<int> v = {1,2,3};
for(auto it = v.cbegin(); it != v.cend(); ++it)
cout << *it << " "; // ✅ 只读访问
- 智能指针:
auto sp = std::make_shared<const int>(42); // 指向 const
const std::shared_ptr<int> csp = std::make_shared<int>(10); // 指针 const
- 模板函数:
template<typename T>
void printArray(const T* arr, size_t n) {
for(size_t i=0;i<n;i++)
cout << arr[i] << " "; // 只读访问
}
35️⃣ 高级实战建议
- 函数参数尽量
const T*或const T&→ 提升安全 - 类成员尽量使用
T* const或const T* const→ 保证稳定性 - STL 容器迭代器尽量使用
const_iterator→ 只读访问 - 智能指针结合 const → 防止资源误修改
- 多级指针 const → 用引用或智能指针替代,避免混淆
如果你愿意,我可以帮你画一张 C++ 指针 const 完整思维导图:
- 三类裸指针 + 双锁模型
- 函数参数示意
- 类成员示意
- STL + 智能指针使用
- 多级指针 const
用箭头 + 锁直观展示 可改性和不可改性,一张图涵盖整个知识体系。
你希望我画吗?

评论