最近在看《C++ Primer》,看到一个非常典型、但又特别容易搞错的知识点:typedef 定义的指针别名,遇上 const 之后到底在修饰谁?
示例代码大概是这样:
cpp
typedef char *pstring;
const pstring cstr = 0;
const pstring *ps;
刚看到时,我下意识地想用"文本替换"的方式理解:把 pstring 换回 char *,结果很快就意识到自己想简单了。这篇笔记就记录一下我把这个问题理顺的过程。
1. 先看 typedef char *pstring;
cpp
typedef char *pstring;
含义很简单:给类型 char * 起了个别名叫 pstring。
关键点是:从这一行开始,pstring 在编译器眼里就是一个"完整的类型" ,就像 int、double 一样。它的真实含义是"指向 char 的指针"。
这一点如果没立住,后面的误解基本都来自于这里。
2. 第一行:const pstring cstr = 0;
cpp
const pstring cstr = 0;
2.1 常见错误:简单文本替换
很多人的第一反应是:
cpp
const pstring cstr = 0;
// 替换 pstring -> char *
const char * cstr = 0; // 看起来像这样
然后就得出结论:
cstr是一个"指向常量字符的指针"(const char*)。
也就是说:指针本身可以改指向,但不能通过它修改字符内容。
这个结论是错的。
2.2 正确理解:const 修饰的是整个别名类型
回到前面那句:pstring 本身是一个指针类型 ,已经等价于 char * 了。
当我写:
cpp
const pstring cstr = 0;
时,const 修饰的是"pstring 这个类型本身",也就是"指向 char 的指针"这个整体。
换成标准写法应该是:
cpp
char * const cstr = 0;
所以真正的含义是:
cstr是一个"常量指针 ",指向char。也就是:指针自己不能改指向,但可以通过它修改指向的字符内容。
2.3 性质总结
-
这是一个 顶层 const:限制的是"对象本身"(这里是指针)不可修改。
-
指向的
char不是 const,可以修改:cppchar ch = 'A'; const pstring cstr = &ch; // 相当于 char * const cstr = &ch; *cstr = 'B'; // OK,改的是 ch 的值 // cstr = nullptr; // ❌ 错误:不能改 cstr 这个指针本身
3. 第二行:const pstring *ps;
cpp
const pstring *ps;
这行看着更抽象,我是按"从变量名往外读"的规则来拆的。
3.1 先看 ps 自己是什么
cpp
const pstring *ps;
// ^ 这里有个 *
*ps说明:ps自己是一个指针;ps没有被const直接修饰,所以ps这个指针本身是可以变来变去的。
也就是说:ps 是一个"二级指针"的感觉。
3.2 ps 指向什么
ps 指向的对象类型是 const pstring。
上一节刚分析过:
cpp
const pstring ≡ char * const
也就是说,被 ps 指向的那个东西,是一个"指向 char 的常量指针"。
所以把整句还原成标准写法,就是:
cpp
char * const *ps;
3.3 用一句话总结
ps是一个指针 ,它指向一个"指向char的常量指针"。
稍微具体一点:
ps本身可以改指向别处;*ps(也就是那个char * const)本身不能改指向;- 但通过
*ps仍然可以修改底层char的内容。
示意代码:
cpp
char ch = 'A';
char *const cstr = &ch; // 常量指针
const pstring *ps = &cstr; // 等价写法:char * const *ps = &cstr;
*(*ps) = 'B'; // OK:改的是 ch
// *ps = some_other_ptr; // ❌:*ps 是 char *const,不能改指向
ps = nullptr; // OK:ps 自己是普通指针,可以改
4. 核心结论:typedef 不是宏替换!
整个问题的根源就在这句:
typedef不是简单的文本替换。
- 它定义的是一个完整的"类型别名";
- 在之上的
const,是修饰这个"别名所代表的类型"; - 对于像
pstring这种"本身就是指针类型"的别名而言,
const pstring自然就变成了"常量指针",而不是"指向常量的指针"。
可以对比下面几种写法,体会差异:
cpp
typedef char *pstring;
const pstring a = 0; // char * const a; 常量指针(顶层 const)
pstring const b = 0; // char * const b; 同上,只是写法不同
const char *c = 0; // 指向 const char 的指针(底层 const)
char const *d = 0; // 同上
char * const e = 0; // 常量指针,指向非 const char
简要对比:
| 写法 | 等价形式 | 说明 |
|---|---|---|
const pstring |
char * const |
常量指针,指针不可改 |
const char * |
char const * |
指向常量的指针,内容不可改 |
char * const |
常量指针,内容可改 |