声明的规范问题

声明由一个类型名加一个变量名以及一个作为结束的分号构成一条声明语句。
根据其声明规范,声明语句左边必须是一个类型名,而右边则是要被声明的变量名字。
变量
int a;
这是一个规范的声明,左边的类型名(int) 决定了 a 是一个 int 类型的对象。
指针
int *p;
这里声明了一个指针,然而,
你不能 *int p; (这是错误的声明方式)
也不能 int p*; (这是错误的声明方式)

总结
无论什么情况,类型名都必须在左边,而右边则是要被声明的变量名。

声明的内存状态

变量
假设这样一句声明:int a;
编译器是这么做的,它遇到 int,则分配一块与 int 一样大小的内存。
接着遇到 a 时,就把这块内存与 a 这个名字绑定在一起。
当你使用 a 时,a 就代表了这块内存。
指针
假设这样一句声明:int *p;
指针,本身用于保存地址,地址只是一串数字。
所以对指针的任何类型声明,都对指针自己本身不起任何作用。
换句话说,指针前面的类型 int 规定了指针只能保存一块内存的地址,而这块内存必须是 int 类型。
例如:
在 32 位的机器上,一个地址是 32 位
char a = 20;
char *p = a;
p 保存的是 a 的 32 位地址,而 a 则必须是 char 类型。
这里有两块内存,一块是 p 的内存,保存了 a 的地址。
另一块内存是 a 的内存空间,保存了 20 这个数据。

在编译时,p 本身被绑定一块内存,* 号告诉编译器这块内存用于保存另一块内存的地址,
而另一块内存则必须是 int 类型。

举个例子:
假设有 这个类型名字。
当你使用它来声明一个指针时:
桶 *p
这时 p 只能指向一个 却不能指向一张 桌子(假设也有 桌子 这个类型的话)

总结
类型,决定了变量能被存储的范围。
指针本身也占用内存,而类型规定了指针只能够指向一块相同类型的内存空间。

const 的作用

分清了以上几个概念,这里就很容易理解了。
如果说类型是规定了一个变量能够被存储的范围。
那么 const 则规定了这个范围内存里的数据是否可被移动(变动)
变量
int const a;
const int a;
我们指定在编译过程中,编译器把一个类型范围的内存和一个变量名绑定在一起。
无论 const 的位置如何变换,类型都是在左边,变量名在右边。
所以以上两条语句是一样的,请看下例:
桶 const a;
它是这么告诉编译器的:
我要申请一个名字为 a 的内存。
这个 a 内存里的数据不可被变动(const)
而且,这个不可变动的内存是 类型。
const 桶 a;
它是这么告诉编译器的:
我要申请一个名字为 a 的内存。
这块内存是 类型的。
而这个类型的内存里的数据不能被改动。

因此,无论是 const int a; 还是 int const a;
两者都是相同的,其最终的 a 都是不能被变动的 int 类型内存空间。
指针
const int *p;
p:我要申请一个名字为 p 的内存
*:这块内存是要用于保存另一块内存的地址。
int:这另一块内存的大小必须符合 int 大小 (例如 4 字节)
const:这块 4 字节内存里的数据不能被改动
int const *p;
p:我要申请一个名字为 p 的内存
*:这块内存是要用于保存另一块内存的地址。
const:这另一块内存里的数据不能被改动
int:这块不能被改变数据的内存大小必须符合 int 大小 (例如 4 字节)

因此:
p 的类型规定了 p 指向的内存的类型,
const 规定了 p 所指向的相同类型的内存空间里的数据不能被改动

于是:
int const *p;
const int *p;
其结果都是指针所指向的的内存空间里的数据不能被改变。
这意味着指针本身可以指向(覆盖保存为)另一个相同类型不同数据的内存空间的地址。

然而:
int *const p;
p:我要申请一个名为 p 的内存。
const:这块内存是不可变动的。
*:这块不可变动的内存是要用于保存另一个内存的地址用的。
int:而这另一个内存地址空间必须为 int 类型。

于是:
p 本身的内存里的数据(所保存的地址) 是不能改变的,
而指向的目标内存必须为 int 类型。
这意味着可以通过 p 去改变目标内存空间里的数据内容。

然后:
你不能 *int const p;(这是错误的声明方式)
也不能 int const p*;(这是错误的声明方式)
int const *pconst int *p (上面已经讲过的)则不同于 int *const p 的意义。
因此,int *const p; 只能有一种写法。
而这种写法唯一不能改变的是指针本身。

既然是唯一的,那么下面这两个语句就很容易理解了:
int const *const p;
const int *const p;

上面讲到了,无论 int constconst int 的位置如何变换,其效果都是相同的,
都是指定指针所指的内存数据不能改变。
*const p 只能有一种写法,且其表明指针本身是常量。
那么最终结果是指针本身不能改变,所指的内存里的数据也不能改变。
请看以下解释:
int const *const p;
p:我想申请一块名为 p 的内存
const:这块内存里的数据不能被改变。
*:这块内存里的数据将要保存的是另一个内存空间的地址。
const:这另一个内存空间里的内容不能被改变。
int:这另一块内存空间的大小必须符合 int 大小。或必须是 int 类型。
const int *const p;
p:我想申请一块名为 p 的内存。
const:这块内存里的数据不能被改变。
*:这块不能被改变的内存空间要保存的是另一个内存空间的地址。
int:这另一块内存空间具有跟 int 一样的大小。
const:这块跟 int 一样大小的内存里的数据不能被修改。