[描述] 在 C、C++、D 和 JavaScript 编程语言中,const是一个类型限定符一个应用于数据类型的关键字,表示数据是只读的。Const - Wiki编程
一个正常的类型定义:编程语言
int num;
复制代码
首先,编译器在读取 int num;
的时候,从左往右读取:spa
int
代表是整型数据,分配 xxx 个字节 ( xxx 根据不一样平台数量不一样 ),num
代表是一个名为 num
的数据 ,;
表示结束 ( 固然有些语言是没有 ;
可是有换行,即读取到换行符,表示结束 ),int num;
最后表示是一个名为 num
且可读写的整型数据。画内存区块:(这很简单,不用解释了~)指针
// 读 (read)
int var = num; // OK
// 写 (write)
num = 100; // OK
/****** RW *****/ /***** RW *****/
/**/ var /**/ <-- read - /**/ num /**/ <-- write - 100
/**************/ /**************/
复制代码
那么加上 const
会变成怎样呢?code
// [] 表示可选
[const] int [const] num;
// 即 有:
const int num; // 第一种
// 或
int const num; // 第二种
// 或 , 这里会报 Duplicate 'const' declaration specifier.
// 即至左向右的第二个 const 不能加。
const int const num; // 第?种
// 因此共 两种 写法。
复制代码
[ 第一种 ] 首先,编译器在读取 const int num;
的时候,从左往右读取:ip
const
代表是只读的数据类型,int
代表是整型数据,分配 xxx 个字节 ( xxx 根据不一样平台数量不一样 ),const int
就代表是一个只读的整型数据;num
代表是一个名为 num
的数据 ,;
表示结束 ( 固然有些语言是没有 ;
可是有换行,即读取到换行符,表示结束 )const int num;
最后表示是一个名为 num
的只读整型数据。画内存区块:内存
// 读 (read)
int var = num; // OK
// 写 (write) 🚫
num = 100; // error
/****** RW *****/ /***** RO *****/
/**/ var /**/ <-- read - /**/ num /**/ <-X- write 🚫 - 100
/**************/ /**************/
复制代码
[ 第二种 ] 首先,编译器在读取 int const num;
的时候,从左往右读取:ci
int
代表是整型数据,分配 xxx 个字节 ( xxx 根据不一样平台数量不一样 ),const
代表是只读的数据类型,int const
就代表是一个只读的整型数据;num
代表是一个名为 num
的数据 ,;
表示结束 ( 固然有些语言是没有 ;
可是有换行,即读取到换行符,表示结束 )const int num;
最后表示是一个名为 num
的只读整型数据。画内存区块:get
// 读 (read)
int var = num; // OK
// 写 (write) 🚫
num = 100; // error
/****** RW *****/ /***** RO *****/
/**/ var /**/ <-- read - /**/ num /**/ <-X- write 🚫 - 100
/**************/ /**************/
复制代码
明显地,第一种和第二种结果是同样的,由于编译器在读取的时候,最后产生的语义是同样的,那么结果固然是同样的。编译器
结:int num;
和 [const] int [const] num;
惟一区别是 num
自身这块内存是否可写,即后者的内存写功能被关闭了。
int * num;
复制代码
首先,编译器在读取 int * num;
的时候,从左往右读取:
int
代表是整型数据,分配 xxx 个字节 ( xxx 根据不一样平台数量不一样 ),*
,因为是在数据定义中存在,即代表是指针类型,int *
, 代表是一个整型的指针类型数据 ,num
代表是一个名为 num
的数据 ,;
表示结束 ( 固然有些语言是没有 ;
可是有换行,即读取到换行符,表示结束 ),int * num;
最后表示是一个名为 num
且保存着可读写的整型的指针数据 ( 保存整型数据内存地址的数据 )。画内存区块:
int no;
int * num = &no;
// *num 读 (read)
int var = *num; // OK, var = no
// *num 写 (write)
*num = 200; // OK
// num 读
int * n1 = num; // OK
// num 写
num = &no; // OK
/****** RW *****/ /************* RW ***********/
/**/ var /**/ <-- read - /**/ no /**/ <-- write - 100
/**************/ /***** RW(对于 num 而言) *****/
⬇️ ⬆️
⬇️(read) ⬆️(write, 200) (*num)
⬇️ ⬆️
/****** RW *****/ /************* RW *************/
/**/ n1 /**/ <-- read - /**/ num /**/ <-- write - &no
/**************/ /******************************/
复制代码
对于 num
指针类型,这里就出现了两个内存区块:
num
自身的内存区块 ( num 保存了 no
变量的内存区块地址 )no
自身的内存区块地址一样的方法加个 const
看看?
从形式上看无非是增长了一个修饰位。
// [] 表示可选
[const] int [const] * [const] num;
// 即 有:
const int * num; // 第一种
// 或
int const * num; // 第二种
// 或
int * const num; // 第三种
// 或
const int * const num; // 第四种, 等价于 int const * const num;
// 或 , 这里会报 Duplicate 'const' declaration specifier.
// 即至左向右的第二个 const 不能加。
const int const * const num; // 第?种
// 因此一共四种写法。
复制代码
[第一种] 首先,编译器在读取 const int * num;
的时候,从左往右读取:
const
代表是只读的数据类型,int
代表是整型数据,分配 xxx 个字节 ( xxx 根据不一样平台数量不一样 ),const int
就代表是一个只读的整型数据;*
,因为是在数据定义中存在,即代表是指针类型数据,int *
, 代表是一个只读的整型的指针类型,num
代表是一个名为 num
的数据 ,;
表示结束 ( 固然有些语言是没有 ;
可是有换行,即读取到换行符,表示结束 ),const int * num;
最后表示是一个名为 num
且保存只读的整型的指针数据。画内存区块:
int no;
const int * num = &no;
// *num 读 (read)
// 右值 *num 可理解成,从 num 中获得 no 的内存地址,再从 no 中读取内容。
int var = *num; // OK
// *num 写 (write) 🚫
// 左值 *num 可理解成,从 num 中获得 no 的内存地址,因为代表了 no 的地址是只读指向,因此没法从 no 中读取内容。
*num = 200; // error
// num 读
int * n1 = num; // OK
// num 写
num = no; // OK
/****** RW *****/ /************* RW ***********/
/**/ var /**/ <-- read - /**/ no /**/ <-- write - 100
/**************/ /***** RO(对于 num 而言) *****/
⬇️ ⬆️
⬇️(read) ⬆️(write, 200 🚫) (*num)
⬇️ ⬆️
/****** RW *****/ /************* RW *************/
/**/ n1 /**/ <-- read - /**/ num /**/ <-- write - &no
/**************/ /******************************/
复制代码
图可能很差理解 ,这里代表的是 num
自身的内存可读写,可是 num
保存的内存地址是只读的。( num 自身的内存地址由系统(程序)保存着 )
即,系统保存着 num
内存地址且可读写,num
保存着 no
的内存地址,但修饰成了只读。
[第二种] 首先,编译器在读取 int const * num;
的时候,从左往右读取:
int
代表是整型数据,分配 xxx 个字节 ( xxx 根据不一样平台数量不一样 ),const
代表是只读的数据类型,int const
就代表是一个只读的整型数据;*
,因为是在数据定义中存在,即代表是指针类型,int const *
, 代表是一个只读的整型的指针类型数据,num
代表是一个名为 num
的数据 ,;
表示结束 ( 固然有些语言是没有 ;
可是有换行,即读取到换行符,表示结束 ),int const * num;
最后表示是一个名为 num
且保存只读的整型的指针数据。第一种和第二种,语义上是同样的因此结果确定是同样的。
[第三种] 首先,编译器在读取 int * const num;
的时候,从左往右读取:
int
代表是整型数据,分配 xxx 个字节 ( xxx 根据不一样平台数量不一样 ),*
,因为是在数据定义中存在,即代表是指针类型,int *
就代表是一个整型的指针类型数据;const
代表是只读的数据类型,int * const
, 代表是一个整型的指针类型的只读的数据,num
代表是一个名为 num
的数据 ,;
表示结束 ( 固然有些语言是没有 ;
可是有换行,即读取到换行符,表示结束 ),int * const num;
最后表示是一个名为 num
且整型的指针类型的只读的数据。画内存区块:
int no;
int * const num = &no;
// *num 读 (read)
// 右值 *num 可理解成,从 num 中获得 no 的内存地址,再从 no 中读取内容。
int var = *num; // OK
// *num 写 (write)
// 左值 *num 可理解成,从 num 中获得 no 的内存地址,再向 no 中写入内容。
*num = 200; // OK
// num 读
int * n1 = num; // OK
// num 写 🚫
num = no; // error
/****** RW *****/ /************* RW ***********/
/**/ var /**/ <-- read - /**/ no /**/ <-- write - 100
/**************/ /***** RW(对于 num 而言) *****/
⬇️ ⬆️
⬇️(read) ⬆️(write, 200) (*num)
⬇️ ⬆️
/****** RW *****/ /************* RO *************/
/**/ n1 /**/ <-- read - /**/ num /**/ <-X- write 🚫 - &no
/**************/ /******************************/
复制代码
[第四种] 首先,编译器在读取 const int * const num;
的时候,从左往右读取:
const
代表是只读的数据类型,int
代表是整型数据,分配 xxx 个字节 ( xxx 根据不一样平台数量不一样 )const int
就代表是一个只读的整型数据,*
,因为是在数据定义中存在,即代表是指针类型,int const *
, 代表是一个只读的整型的指针类型的数据,const
代表是只读的数据类型,int const * const
代表是一个只读的整型的指针类型的只读的数据num
代表是一个名为 num
的数据 ,;
表示结束 ( 固然有些语言是没有 ;
可是有换行,即读取到换行符,表示结束 ),int * const num;
最后表示是一个名为 num
且只读的整型的指针类型的只读的数据。画内存区块:
int no;
int * const num = &no;
// *num 读 (read)
// 右值 *num 可理解成,从 num 中获得 no 的内存地址,再从 no 中读取内容。
int var = *num; // OK
// *num 写 (write) 🚫
// 左值 *num 可理解成,从 num 中获得 no 的内存地址,因为代表了 no 的地址是只读指向,因此没法从 no 中读取内容。
*num = 200; // error
// num 读
int * n1 = num; // OK
// num 写 🚫
num = no; // error
/****** RW *****/ /************* RW ***********/
/**/ var /**/ <-- read - /**/ no /**/ <-- write - 100
/**************/ /***** RO(对于 num 而言) *****/
⬇️ ⬆️
⬇️(read) ⬆️(write, 200 🚫 ) (*num)
⬇️ ⬆️
/****** RW *****/ /************* RO *************/
/**/ n1 /**/ <-- read - /**/ num /**/ <-X- write 🚫 - &no
/**************/ /******************************/
复制代码
/// 基本类型
// num 的内存可读写
int num;
// num 的内存只读,不可写
const int num; // 等价于 int const num;
/// 一级指针类型
// num 自身内存可读写; num 指向的内存可读写
int * num;
// num 自身内存可读写; num 指向的内存只读,不可写
const int * num; // 等价于 int const * num;
// num 自身内存只读,不可写; num 指向的内存可读写
int * const num;
// num 自身内存只读,不可写; num 指向的内存只读,不可写
const int * const num;
复制代码
const
修饰无论放那都是只读。( 由于从语义上看无论 const
放那它也只有一种结果 )const
一旦紧挨着变量名,即自身的内存只读,其它状况都是指向的内存只读。*
,那么要考虑的就是 ( x + 1 ) 个内存块的问题了。int const
和 const int
,编译器都会直接转换成 const int
。二级指针类型的例子:
int N = 8;
int const NC = 8;
int * n;
int * const n0 = NULL;
int const * n1 = NULL;
int const * const n2 = NULL;
int const ** const n3 = NULL;
int * const * const n4 = NULL;
// [] 表示可选
// [const] int [const] * [const] * [const] num;
// num 自身的内存,*num 指向的内存, **num 指向的内存,一共三块内存
// num 的数据类型是:int ** const, *num 的数据类型是:int * [const], **num 的数据类型是:int .
// const 修饰 num 自身内存,即 num 自身的内存只读
int ** const num = NULL;
**num = N; // OK
*num = n; // OK
*num = n0; // OK
num = &n0; // error 🚫
// num 自身的内存,*num 指向的内存, **num 指向的内存,一共三块内存
// num 的数据类型是:int * [const] * , *num 的数据类型是:int * const, **num 的数据类型是:int .
// const 修饰 *num 指向的内存,即 *num 指向的内存只读
int * const * num;
**num = N; // OK
*num = n0; // error 🚫
num = &n0; // OK
num = &n; // OK
// num 自身的内存,*num 指向的内存, **num 指向的内存,一共三块内存
// num 的数据类型是:int const **, *num 的数据类型是:int [const] * [const], **num 的数据类型是:int const .
// const 修饰 **num 指向的内存,即 **num 指向的内存只读
int const ** num;
**num = NC; // error 🚫
*num = n; // OK
*num = n0; // OK
*num = n1; // OK
*num = n2; // OK
num = n3; // OK
// num 自身的内存,*num 指向的内存, **num 指向的内存,一共三块内存
// num 的数据类型是:int * const * const, *num 的数据类型是:int * const, **num 的数据类型是:int .
// 最右边的 const 修饰 num 自身内存,即 num 自身的内存只读
// const 修饰 *num 指向的内存,即 *num 指向的内存只读
int * const * const num = NULL;
**num = N; // OK
*num = n0; // error 🚫
num = n4; // error 🚫
复制代码
若是对上面的内容有疑惑,那么就动手画画内存图~~吧 !!!