const指针

指针本质上也是一个整型的变量, 但是声明指针的时候如果遇到 const 修饰, 情况会略复杂一点。

疑问

在阅读本文之前,请先看以下几个声明语句:

1
2
3
4
5
6
7
8
int a = 10;
int const *d = &a; // 0
const int* b = &a; // 1
int* const c = &a; // 2
const int* const p = &a; // 3
const int const *p2 = &a; // 4

上述声明的指针变量, 哪些指针是常量, 哪些指针指向的内容是常量, 或者哪些指针和其指向的内容都是常量?
如果你也不是很清楚,请跟着我一起回顾下 C 语言的基础知识吧!

指针声明的语法

我们先从声明指针变量的语法来讲,一般声明一个指针变量的语法为:

1
type * p = xxx;

主要存在两种写法:

1
2
type* p = xxx;
type *p = xxx;

一般人都倾向于将 * 写在靠近类型的一侧, 也就是第一种声明方式, 我自己也是如此。
这种声明方式的好处是,让人一下子就注意到这个变量是指向某种类型的指针。
但是,如果你对指针声明的知识理解的不到位的话,很多时候,采用第1种声明方式是有风险的。
比如以下这句

1
int* a, b, c;

就很容易造成误解, 有的人可能会误以为这是三个 int 型指针,而编码者本身的意图可能也是如此,但是事实并非如此。
它的含义是,a 为指向 int 变量的指针,b 和 c 均为一个 int 型变量。
如果采用方式 2 来声明的话,就会明显一点。

1
int *a, b, c;

有两个解决的方法:

  • 每个变量都单独声明
  • 使用 typedef 定义指针类型,再使用该类型来声明指针

解析

接下来,我们分解以下声明语句,之后大家就会有一个清晰的认识了。
其实,理解指针声明的关键在于理解这个 *
`是间接访问操作符,它只能间接引用指针变量;a` 本身就是一个表达式,表示间接访问a指向的内存单元
接下来,我们依据这个理念来理解 const 指针的声明方式。
语句一

1
const int* b = &a;

它等价于

1
const int (*b) = &a;

*b 表明 b 是一个指针变量
int (*b) 表明通过 b 能够间接访问到一个整型变量,也就是说 b 为一个整型的指针
const int (*b) 则表明该整型变量是一个 const 常量。
所以,b 是一个 int 类型的指针,它本身不是常量;它指向的整型变量为常量

语句二

1
int* const c = &a;

它等价于

1
int *(const c) = &a;

const c 说明 c 是一个常量
*(const c) 说明这个常量是一个指针
最后,int 关键字表明这个指针指向一个整型变量。
所以,c 是一个 int 型指针,这个指针存储的地址值是常量,不可更改;c 指向的整型内存单元不是常量,可更改
语句三、四
它们是等价的,上述举例只是为了引出两种声明的写法。我们选其中一句来说明。

1
const int* const p1 = &a;

它等价于

1
const int (*(const p1)) = &a;

const p1 表明 p1 是一个常量;
*(const p1) 表明 p1 是一个指针;
int *(const p1) 表明 p1 是一个整型指针;
const int *(const p1) 则跟语句一是类似的,表明 p1 指向的内存单元也是一个常量。
所以,这里p1和p1指向的内存单元都是常量

结束语

至此,用 const 来声明指针的几种情况均已经阐述完毕。其中,对于声明语句 0 我没有进行分解,留给自己复习检查用~