名称说明:关于 常量指针 和 指针常量 各个参考书目的叫法会有所区别,本文采用较符合语法的叫法: 指向常量的指针 和 常量指针。

字面解读:

  • 指向常量的指针:有一个变量(也有可能是const修饰的变量)当被一个指针指向时,不能通过该指针改变这个变量,则这个指针称之为“指向常量的指针”, 这不意味着 指针不能改变,也不意味着这个变量不能改变,只是说,不能通过这个指针改变这个变量的值
  • 常量指针:具有常量性质的指针;首先有一个指针,这个指针具有常量的性质,即指针的值(或说指针的指向)不能改变,但可以通过指针去改变指向的变量的值。

1. const 限定一个变量时 

问题:const int a = 10; a是否可以改变?

答案: c语言中允许改变,c++中不允许改变.

c语言改变一个const变量的方式:

#include <stdio.h>

int main() {

/*
    const 修饰的变量成为只读变量 常量
    可以通过一级指针修改
    */

    const int a = 10;
    //a = 100; // err
    printf("a的值:%d\n", a); // a的值:10

    int *p = &a;

    *p = 100;

    printf("a的值:%d\n", a); // a的值:100

    return 0;
}

c++改变一个const变量,但不成功的例子:

#include <iostream>

int main() 
{
    const int a = 10;
    std::cout << "a = " << a << ", \t &a = " << &a << std::endl;

    // 尝试1:改变 a 的值 失败
    //a = 100;      // err: 不能改变一个常量类型的变量

    // 尝试2:改变 a 的值 失败
    //int* p = &a;         // err: c++中,不能从 const int* 默认转换成 int*
    //int* p = (int*)(&a); // ok: c++中,可以从 const int* 强制 转换成 int* // 但不建议这样写。
    int* p = const_cast<int*> (&a);
    std::cout << "a = " << a << ", \t &a = " << &a << ", \t p = " << p << ", \t *p = " << *p << ", \t &p = " << &p << std::endl;

    *p = 100; // 指针p 拿到了 a变量的地址,*p表示的就是 a, 但在 c++中这句话并不能改变a的值。//无效但不报错的一句话 //不建议这么写

    std::cout <<" ---- after *p = 100 ----" << std::endl;
    std::cout << "a = " << a << ", \t &a = " << &a << ", \t p = " << p << ", \t *p = " << *p << ", \t &p = " << &p << std::endl;

    return 0;
}

可以看出, 即使我们拿到了 常量 a的地址(const int_),然后去掉const ,得到一个指针(int _)然后对这个指针的值进行改变(*p = 100)可以看出 a的值没有变化,虽然 p的值(指向的位置)仍然是a的地址。

2. const 限定一个指针时

当然 指针 也是一种变量。只不过,作为指针类型的变量,它存的值是地址(数值 如0X0020DF)。

对比 overview:

2.1 指向常量的指针 const int* p;

// 指向常量的指针: 你可以随便指向一个变量,但是你不能通过该指针改变指向的那个变量的值,
// 用途:防止通过指针 去改变 指向的变量的值
// 比如用在:函数参数列表中(当不希望该指针指向的变量被改变时)

// 指向常量的指针
void points_to_constants(void) {
    // 指向常量的指针: 指针的值可以改变,但是不能通过该指针 改变 指向的变量的值
    int a = 1;
    const int b = 2;
    int c = b;

    // 指向常量的指针: 你可以随便指向一个变量,但是你不能改变你指向的那个变量的值,
    // 用途:防止通过指针 去改变 指向的变量的值
    // 比如用在:函数参数列表中(当不希望该指针指向的变量被改变时)
    const int *p1 = &a; // ok, 等价于 int const * p1 = &a;
    const int *p2 = &b; // ok,
    const int *p3 = &c; // ok, 也可以先定义,再初始化一个值 const int *p3; p3 = &c;

    std::cout << "p1: " << *p1 << std::endl; // p1: 1
    std::cout << "p2: " << *p2 << std::endl; // p2: 2
    std::cout << "p3: " << *p3 << std::endl; // p3: 2

    // *p1 = 10; // ERR: 不能改变 "指向常量的指针(p1) 所指向的变量(a)的值"  // <<============
    a = 10;      // OK: 改变指向的变量的值,即使改变 *p1 (代表了a) 的值
    std::cout << "p1: " << *p1 << std::endl; // p1: 10

    p1 = p2;     // OK: 改变该指针的值                                 // <<============
    c = 20;

    std::cout << "p1: " << *p1 << std::endl; // p1: 2
    std::cout << "p2: " << *p2 << std::endl; // p2: 2
    std::cout << "p3: " << *p3 << std::endl; // p3: 20

    char str[] = "ab cd";
    change_str_no(str);     // 打印 [a][b][ ][c][d]
    change_str_yes(str);   // 打印 [a][b][-][c][d]
}

void change_str_no(const char *str) {
    while (*str) {
        if (*str == ' ') {//遇到空格修改成—
            //*str = '-';//这里修改了常量指针所指向的数值。这是不允许,所以报错。 // <<-----------
        }
        printf("[%c]", *str);
        str++;
    }
    printf("\n");
}

void change_str_yes(char *str) {
    printf("========\n");
    while (*str) {
        if (*str == ' ') {//遇到空格修改成—
            *str = '-';//这里修改了指针所指向的值。这是允许的
        }
        printf("[%c]", *str);
        str++;
    }
    printf("\n");
}

2.2 常量指针 int* const p = &a;

// 常量指针: 指针的值(指向)不能改变,但是能通过 常量指针 改变 指向的那个变量 的值,当然 被指向的那个变量的值也可以改变
// 用途:约束指向关系,指向关系不能变

// 常量指针 (具有常量性质的指针) (类似于引用:一旦绑定,终身有效)
void const_pointers() {
    int a = 1;
    const int b = 2;
    int c = b;

    // 常量指针: 指针的值(指向)不能改变,但是能通过 常量指针 改变 指向的那个变量 的值,当然 被指向的那个变量的值也可以改变
    // 用途:约束指向关系,指向关系不能变
    // 比如用在:??
    int * const p1 = &a; // ok,
    //int * const p2 = &b; // ERR: 不能用常量指针,指向一个常量(不能改变的变量) // error C2440: “初始化”: 无法从“const int *”(&b) 转换为 “int *” (p2)
    int * const p3 = &c;   // ok, 定义一个常量指针时,必须直接初始化 //ERR:  int * const p3; p3 = &c;

    std::cout << "p1: " << *p1 << std::endl; // p1: 1
    std::cout << "p3: " << *p3 << std::endl; // p3: 2

    *p1 = 10;      // OK: 能通过 常量指针(p1) 改变 所指向的变量(a)的值"  // <<============
    std::cout << "p1: " << *p1 << std::endl; // p1: 10

    a = 100;       // OK: 改变指向的变量的值,即使改变 *p1 (代表了a) 的值
    std::cout << "p1: " << *p1 << std::endl; // p1: 100

    //p1 = p3;     // ERR: 不能改变 常量指针 的值(的指向)            // <<============
    c = 20;        // OK: 常量指针 指向的变量 的值 改变了,*p3(代表了c) 自然也变了

    std::cout << "p3: " << *p3 << std::endl; // p3: 20
}

3. 只读指针(指向常量的常量指针)

// 只读指针: 指针的值(指向)不能改变,指针所指向的那个变量可以改变,不能通过指针改变指向的变量的值
// 用途:只从指针中读取指向某变量的值,而不能对这个变量进行写操作。

// 只读指针 (指向常量的常量指针)
void read_only_pointer() 
{
    int a = 1;
    const int b = 2;
    int c = b;

    // 只读指针: 指针的值(指向)不能改变,指针所指向的那个变量可以改变,不能通过指针改变指向的变量的值
    // 用途:约束指向关系,指向关系不能变
    const int *const p1 = &a; // ok,
    const int *const p2 = &b; // ERR: 不能用常量指针,指向一个常量(不能改变的变量) // error C2440: “初始化”: 无法从“const int *”(&b) 转换为 “int *” (p2)
    const int *const p3 = &c;   // ok, 定义一个常量指针时,必须直接初始化 //ERR:  int * const p3; p3 = &c;

    std::cout << "p1: " << *p1 << std::endl; // p1: 1
    std::cout << "p2: " << *p2 << std::endl; // p2: 2
    std::cout << "p3: " << *p3 << std::endl; // p3: 2

    // *p1 = 10; // ERR: 不能通过只读指针改变 指向的变量 的值    // 指向常量的指针 的性质
    // p1 = p2;  // ERR: 不能改变指针的值(指向)               // 常量指针 的性质

    a = 10;      // OK:  可以通过改变 被指向变量的值,来改变 *p1 // <<============
    //b = 20;      // ERR: b 本身是常量,不能改变
    c = 30;      // OK:  可以通过改变 被指向变量的值,来改变 *p2 // <<============

    std::cout << "p1: " << *p1 << std::endl; // p1: 10
    std::cout << "p2: " << *p2 << std::endl; // p2: 2
    std::cout << "p3: " << *p3 << std::endl; // p3: 30

    int e = *p1 + *p2 + *p3; // OK: 只能读 该指针 指向的变量 的值
    std::cout << "e: " << e << std::endl; // p3: 42 (= 10 + 2 + 30)
}