关于指针常量与常量指针

指针常量

Copy
int *const ptr = nullptr;

const 修饰的是指针 ptr 的值，ptr 本身不能被修改，所以指针常量必须初始化，同时我们无法对 ptr 的值进行修改。

Copy
int a = 10;
int *const ptr = &a;
ptr = nullptr; // error

指针常量的应用场景

例如：需要编写一个函数，实现使用传参指针的形式交换两个整形的值。

下面是实现代码：

Copy
void swap(int *a, int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}

上述代码可以实现功能，但是存在一定的问题，因为我们可以在函数体内可以修改指针的所指向的值，同时也可以修改指针的值（这并不是我们所希望的），为了使代码更为健壮，我们可以使用指针常量，使得传入指针的值不会被无意修改。

下面是修改后的代码：

Copy
void swap(int *const a, int *const b) { ... }

常量指针

Copy
const int *ptr;
// 或
int const *ptr;

const 修饰的 *ptr 的值，也就是指针所指向的值，ptr 指向的值是不可修改的。同时在不进行常量类型转换的情况下我们无法对 *ptr 进行修改。

Copy
int a = 10;
int const *ptr = &a;
*ptr = 20; // error

常量指针的应用场景

例如：需要编写一个函数，实现打印一个数组。

下面是实现代码：

Copy
void printArray(int const* arr, size_t len) {
for (size_t i = 0; i < len; ++i) {
std::cout << arr[i] << " ";
}
}

这里你可能会认为使用 const 修饰数组退化而来的指针所指向的值就可以了，但是这仍存在的问题，因为我们仍可以在函数体内修改指针的值，这并不是我们希望的，因此在此基础上还需要 const 修饰指针的值。

下面是修改后的代码：

Copy
void printArray(int const *const arr, size_t len) { ... }

指针常量与常量指针的关系

我们会发现常量指针有两种写法：int const * 和 const int * 。

对于第一种形式的理解：const 是向左修饰的，例如：int const *ptr; 中 const 修饰的是 int 值类型，所以指针指向的值不可以修改；int *const ptr = nullptr; 中 const 修饰 int * 指针类型，所以指针指向不能修改。这种理解需要遵守的代码规范，因为如果将常量指针写成 const int * 就需要我们进行转换了。（也可以理解为如果左侧没有对象，则转为修饰右侧对象。）

常量指针和指针常量的命名则是与 const int * 和 int *const 的代码规范相对应。

观察以下代码：

Copy
#include <iostream>

struct A {
void foo() { std::cout << "non const" << std::endl; }
void foo() const { std::cout << "const" << std::endl; }
};

int main() {
A a;
A *const ptr1 = &a;
ptr1->foo();
A const *ptr2 = &a;
ptr2->foo();
}

int *const ptr1 = &a 中 const 修饰的是 ptr1 指针的值，没有修饰指针所指向的对象，所以 ptr1->foo() 调用非 const 版本。

int const *ptr2 = &a 中 const 修饰的是 ptr2 指针指向的值 a，意思是指向的值不可修改，所以 ptr2->foo() 调用 const 版本。

如有纰漏，欢迎指正。