C++ 中 typedef 指针别名与 const 的坑

最近在看《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 在编译器眼里就是一个"完整的类型" ,就像 intdouble 一样。它的真实含义是"指向 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,可以修改:

    cpp 复制代码
    char 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 常量指针,内容可改

相关推荐
想回家的一天5 小时前
ECONNREFUSED ::1:8000 前端代理问题
开发语言
cike_y5 小时前
Mybatis之解析配置优化
java·开发语言·tomcat·mybatis·安全开发
Jay_Franklin6 小时前
SRIM通过python计算dap
开发语言·python
Slow菜鸟6 小时前
Java基础架构设计(三)| 通用响应与异常处理(分布式应用通用方案)
java·开发语言
消失的旧时光-19436 小时前
401 自动刷新 Token 的完整架构设计(Dio 实战版)
开发语言·前端·javascript
wadesir6 小时前
Rust中的条件变量详解(使用Condvar的wait方法实现线程同步)
开发语言·算法·rust
tap.AI7 小时前
RAG系列(二)数据准备与向量索引
开发语言·人工智能
阿蒙Amon7 小时前
C#每日面试题-重写和重载的区别
开发语言·c#
是一个Bug7 小时前
Java基础20道经典面试题(二)
java·开发语言
Z_Easen7 小时前
Spring 之元编程
java·开发语言