引言:为什么这个话题甚至让经验丰富的 C++ 开发者抓狂?
\n\n如果你曾经打开过别人的 C++ 代码,看到类似 const int * const data 的东西,你肯定有过关闭编辑器去喝茶的冲动。这种奇怪的语法,const 散落在星号两侧,真是一块绊脚石。问题 “const int*、const int * const 和 int * const 之间有什么区别?” 在 Stack Overflow 上获得了数十万次浏览。这并非偶然。
理解这些结构不仅仅是学究的任性。这是编写安全、自文档化代码的关键,更重要的是,通过面试的关键。今天,我们将一劳永逸地解决这个困惑,使用一个始终有效的简单规则。
\n\n三大支柱:我们要比较什么?
\n\n在 C++ 中,指针和常量性可以有四种组合方式(其中三种是最常见的)。让我们来定义它们:
\n\n- \n
- 变体 1:
const int* ptr(或int const* ptr——这是相同的)。 \n - 变体 2:
int * const ptr。 \n - 变体 3:
const int * const ptr(前两者的组合)。 \n
关键思想:const 总是修饰它左边的内容。如果左边没有内容,则修饰它右边的内容。听起来很简单,但在实践中,这条规则非常有用。
\n\n变体 1:const int* ptr(指向常量整数的指针)
\n\n从右向左读:“ptr 是一个指针,指向一个整数,该整数是常量”。
\n\n这在实践中意味着什么?
\n- \n
- 你不能通过 ptr 修改它指向的变量的值。 \n
- 你可以修改指针本身(让它指向另一个变量)。 \n
#include <iostream>\n\nint main() {\n int a = 10;\n int b = 20;\n\n const int* ptr = &a; // ptr — 指向 const int 的指针\n\n // *ptr = 15; // 编译错误!不能通过 const int* 修改值\n std::cout << "*ptr = " << *ptr << std::endl; // 输出 10\n\n ptr = &b; // 可以!修改指针本身\n std::cout << "*ptr = " << *ptr << std::endl; // 输出 20\n\n return 0;\n}\n\n这种类型常用于向函数传递数据,当你希望确保函数不会破坏原始数据时。例如,字符串 void print(const int* data) 是一个“只读”承诺。
变体 2:int * const ptr(指向整数的常量指针)
\n\n从右向左读:“ptr 是一个常量指针,指向一个整数”。
\n\n这里正好相反:
\n- \n
- 你可以修改 ptr 指向的变量的值。 \n
- 你不能修改指针本身(它必须始终指向同一块内存区域)。 \n
#include <iostream>\n\nint main() {\n int a = 10;\n int b = 20;\n\n int * const ptr = &a; // ptr — 指向 int 的常量指针\n\n *ptr = 15; // 可以!通过指针修改值\n std::cout << "*ptr = " << *ptr << std::endl; // 输出 15\n\n // ptr = &b; // 编译错误!不能修改指针本身\n\n return 0;\n}\n\n可以把 int * const 想象成一个“绑定”的指针。它常用于操作硬件寄存器,或者当指向缓冲区的指针不应“逃逸”到其他位置时。
变体 3:const int * const ptr(指向常量整数的常量指针)
\n\n从右向左读:“ptr 是一个常量指针,指向一个整数,该整数是常量”。