在 C/C++ 中,const 关键字与指针结合使用时,会产生不同的效果,主要是 const 指针指向 const 的指针 两种情况。这两者在语法、含义和使用场景上有着明显的区别。以下是对这两种指针的详细讲解:

1. 指向 const 的指针

定义:指向 const 对象的指针,表示指针指向的内存中的数据是只读的(不能通过该指针修改)。但指针本身的值(即指向的地址)是可以改变的。

语法

const T *ptr;  // 或 T const *ptr;
  • T 是数据类型(如 int, char 等)。
  • const 修饰的是 *ptr,表示指针指向的内容是常量。
  • 这种指针可以指向任何 T 类型的数据(包括非 const 数据),但通过该指针无法修改指向的数据。

特点

  • 不能通过指针修改指向的数据*ptr = value 会导致编译错误。
  • 指针本身可以改变:可以让 ptr 指向其他地址。
  • 可以指向非 const 数据:即使指向非 const 数据,也只能以只读方式访问。

示例代码

#include <stdio.h>

int main() {
    int a = 10;
    int b = 20;

    const int *ptr = &a; // 指向 const 的指针
    // *ptr = 15;       // 错误:不能通过 ptr 修改 a 的值
    ptr = &b;          // 正确:可以改变 ptr 指向的地址
    printf("%d\n", *ptr); // 输出 20

    return 0;
}

使用场景

  • 当函数参数需要传递指针,但不希望通过该指针修改数据时,常用 const T *
    例如:
    void printValue(const int *p) {
        // *p = 100; // 错误:不能修改
        printf("%d\n", *p);
    }

2. const 指针

定义:指针本身是常量,表示指针的地址不可改变(即不能指向其他地址),但指针指向的数据是可以修改的(如果数据本身不是 const)。

语法

T * const ptr = &some_variable;
  • const 修饰的是 ptr,表示指针本身是常量。
  • 必须在定义时初始化,因为 const 指针不能重新赋值。
  • 通过该指针可以修改指向的数据(只要数据本身不是 const)。

特点

  • 指针的地址不可变:不能让 ptr 指向其他地址。
  • 可以通过指针修改数据*ptr = value 是合法的(如果数据本身不是 const)。
  • 必须初始化:定义 const 指针时必须指定初始地址。

示例代码

#include <stdio.h>

int main() {
    int a = 10;
    int b = 20;

    int * const ptr = &a; // const 指针
    *ptr = 15;         // 正确:可以修改 a 的值
    // ptr = &b;       // 错误:不能改变 ptr 的指向
    printf("%d\n", *ptr); // 输出 15

    return 0;
}

使用场景

  • 当需要确保指针始终指向同一地址,但允许修改该地址的数据时使用。
  • 例如,固定指向某个全局变量或数组的指针。

3. const 指针指向 const 数据

定义:结合上述两种情况,指针本身是常量,且指向的数据也是常量。

语法

const T * const ptr = &some_variable;
  • 第一个 const 修饰 *ptr,表示不能通过指针修改数据。
  • 第二个 const 修饰 ptr,表示指针本身不能改变指向。
  • 必须在定义时初始化。

特点

  • 指针不可变:不能让 ptr 指向其他地址。
  • 数据不可变:不能通过 ptr 修改指向的数据。
  • 提供最高级别的限制,适用于完全不可修改的场景。

示例代码

#include <stdio.h>

int main() {
    int a = 10;
    const int * const ptr = &a; // const 指针指向 const 数据
    // *ptr = 15;     // 错误:不能修改数据
    // ptr = &b;      // 错误:不能修改指针
    printf("%d\n", *ptr); // 输出 10

    return 0;
}

使用场景

  • 当需要完全锁定指针及其指向的数据时使用,例如在嵌入式系统中操作硬件寄存器。

4. 总结与对比

类型语法指针地址可变数据可通过指针修改使用场景示例
指向 const 的指针const T *ptr函数参数传递,保护数据不被修改
const 指针T * const ptr是(非 const 数据)固定指针指向的地址
const 指针指向 constconst T * const ptr完全锁定指针和数据,硬件寄存器

5. 记忆技巧

  • 阅读顺序:从右到左读指针声明:
    • const int *ptr*ptrconst int,即指针指向的是常量 int
    • int * const ptrptrconst,即指针本身是常量。
    • const int * const ptr*ptrconst int,且 ptrconst,两者都不可变。
  • const 位置
    • const* 之前,修饰的是指向的数据。
    • const* 之后,修饰的是指针本身。

6. 注意事项

  1. 指向非 const 数据的 const 指针
    • const int *ptr 可以指向 intconst int,但不能通过 ptr 修改数据。
    • 反过来,int *ptr 不能指向 const int(会丢失 const 限定)。
  2. const 指针的初始化
    • T * const ptr 必须在定义时初始化,否则无法使用。
  3. 多级指针
    • 对于多级指针(如 int **ptr),const 可以修饰每一级,规则类似。例如:
      const int **ptr;      // 指向 const int* 的指针
      int * const *ptr;     // 指向 int* 的 const 指针

7. 实际应用

  • 函数参数const T * 常用于函数参数,防止函数修改传入的数据。例如 strlen(const char *str)
  • 类成员函数:在 C++ 中,const 指针常用于类成员函数的参数或返回值,以保护对象状态。
  • 嵌入式系统const T * const 常用于硬件寄存器操作,确保地址和数据都不被意外修改。

如果有更具体的代码或场景需要分析,请提供,我可以进一步讲解!