常量指针与指针常量

学习指针常量与常量指针,最关键的是需要用到 const 关键字。

const 是单词 constant 的缩写,翻译过来就是恒定的、不变的,也被称为 常量限定符,表示只读的意思。习惯性的使用 const,可以避免在代码中对某些不应修改的数据造成可能的改动。

在 C 语言中,常量有以下 4 种表示方法:

  1. 字面常量:直接使用数字、字符表示的量

    8          // 整型常量
    3.14       // 浮点数常量
    '@'        // 字符常量
    "abc"      // 字符串常量
  2. #define 宏定义的标识符常量

    #define PINK    0xFFC0CB
    #define BLUE    0x0000FF
    #define D       double
    #define HELLO   printf("Hello, world!");
  3. enum 枚举常量

    enum weekdays {
    MON = 1,
    TUE = 2, 
    WED = 3, 
    THU = 4,  
    FRI = 5, 
    SAT = 6, 
    SUN = 7
    };
  4. const 修饰的只读变量

    const int a = 100;
    const char name[] = "罗大富";

const 修饰变量与数组

const 最简单的应用场景就是修饰基本数据类型,对于基本的数据类型,修饰符 const 可以用在类型说明符前,也可以用在类型说明符后,其结果是一样的,代码如下:

int const a = 100;
const int a = 100;              //与上面等价

int const array[3] = {1,2,3};
const int array[3] = {1,2,3};   //与上面等价

const 变量不可修改,所以定义时必须初始化,并且初始化完成后,变量的值无法修改,例如,如果把以上代码中的:

const int a = 10;       // 声明只读变量 a
a = 100;                // 修改只读变量 a 的值

运行以上代码,就会出现报错提示:

error:只读变量赋值
error:只读变量赋值

const 修饰指针变量 - 常量指针与指针常量

const 在指针变量的修饰方面可以存在于两个位置的修饰,即指针常量和常量指针。可以根据 const* 的位置关系判断 const 是修饰数据还是指针本身:

  • const 位于 * 的左边,则表示指向的数据为常量,也被称为 常量指针,例如:const int* p1
  • const 位于 * 的右边,则表示指针为常量,其指向的地址不可改变,也被称为 指针常量,例如:int* const p2

例如:首先创建一个数组 array,然后让普通指针 pointer_1 指向该数组,此时,我们通过指针既可以修改指针的地址,也可以修改指针指向地址存储的数据:

int array[] = {1, 2, 3, 4, 5};

// 定义一个普通指针指向数组 array 的首地址
int* ptr_1 = array;
printf("ptr_1 指向的地址为:%p, 对应的值为:%d\n", ptr_1, *ptr_1);

// 通过指针修改数组元素的值
*ptr_1 = 100;
printf("通过指针修改数据后,ptr_1 指向的地址为:%p, 对应的值为:%d\n", ptr_1, *ptr_1);

// 修改指针指向的地址
ptr_1 = &array[1];
printf("通过指针修改地址后,ptr_1 指向的地址为:%p, 对应的值为:%d\n", ptr_1, *ptr_1);

然后,创建一个常量指针 pointer_2 使其指向数组 array,代码如下:

// 常量指针
const int* ptr_2 = array;
printf("ptr_2 指向的地址为:%p, 对应的值为:%d\n", ptr_2, *ptr_2);

// 通过指针修改数组元素的值
*ptr_2 = 200;               // 错误使用

// 修改指针指向的地址
ptr_2 = &array[2];          // 正确使用
printf("通过指针修改地址后,ptr_2 指向的地址对应的值为:%d\n", *ptr_2);

通过上述代码可以发现:const 位于星号 * 左侧时,const 就是用来修饰指针所指向的变量,即指针指向为常量。虽然指针指向的地址对应的值不可以修改,但是可以修改其指向的地址。

接着,再创建一个指针常量 pointer_3 使其指向数组 array,代码如下:

// 指针常量
int* const ptr_3 = array;
printf("ptr_3 指向的地址为:%p, 对应的值为:%d\n", ptr_3, *ptr_3);

// 通过指针修改数组元素的值
*ptr_3 = 300;               // 正确用法
printf("通过指针修改数据后,ptr_3 指向的地址为:%p\n", *ptr_3);

// 修改指针指向的地址
ptr_3 = &array[3];

通过上述代码可以发现:const 位于星号 * 的右侧,const 就是修饰指针本身,即指针本身是常量,指针存储的值不可修改,而指针存储的值是地址,所以说常量指针不可以修改地址。虽然指针的地址不可以修改,但是可以通过指针修改它所指向的变量的值。

最后,再创建一个指向常量的指针常量 pointer_4,代码如下:

// 指向常量的指针常量
const int* const pointer_4 = array;

*pointer_4 = 400;               // 错误使用
pointer_4 = &array[4];          // 错误使用

pointer_4 既不可以修改其指向的地址,也不可以修改其指向的地址对应的值。

区分指针常量和常量指针的技巧

名字从左向右读,例如:

  • const int* pointer_2const(常量) 在左侧,*(指针) 在右侧,因此,名字就是常量指针。
  • int* const pointer_3*(指针) 在左侧,const(常量) 在右侧,因此,名字就是指针常量。

在判断功能时,可以把 * 看作是解引用运算符,比较 const* 谁离指针更近,例如:

  • const int* pointer_2* 先对 pointer_2 进行解引用操作,也就变成了该指针指向的变量,const 将该指针指向的变量变成了只读的,也就是我们可以修改指针的地址,但是不能修改指针指向的变量的值;
  • int* const pointer_3const 先把该指针变量,变成了只读的,而指针存储的值是地址,也就是说我们不可以修改指针指向的地址,但是可以修改该地址对应的值。