Const
Const 特點
[描述] 在 C、C++、D 和 JavaScript 程式語言中,const是一個型別限定符一個應用於資料型別的關鍵字,表示資料是隻讀的。Const - Wiki
- 它是一個預設關鍵字
- 它是一個修飾符(型別限定符:專門用於修飾資料型別的修飾符)
- 它修飾的資料是只讀的
Const 作用推導
基本型別 - 讀寫
一個正常的型別定義:
int num;
複製程式碼
首先,編譯器在讀取 int num;
的時候,從左往右讀取:
- 先是
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
會變成怎樣呢?
基本型別 - 只讀
// [] 表示可選
[const] int [const] num;
// 即 有:
const int num; // 第一種
// 或
int const num; // 第二種
// 或 , 這裡會報 Duplicate 'const' declaration specifier.
// 即至左向右的第二個 const 不能加。
const int const num; // 第?種
// 所以共 兩種 寫法。
複製程式碼
[ 第一種 ] 首先,編譯器在讀取 const int num;
的時候,從左往右讀取:
- 先是
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;
的時候,從左往右讀取:
- 先是
int
表明是整型資料,分配 xxx 個位元組 ( xxx 根據不同平臺數量不同 ), - 再到
const
表明是隻讀的資料型別, - 這時候
int const
就表明是一個只讀的整型資料; - 再
num
表明是一個名為num
的資料 , - 再讀取到
;
表示結束 ( 當然有些語言是沒有;
但是有換行,即讀取到換行符,表示結束 ) - 那麼
const int num;
最後表示是一個名為num
的只讀整型資料。
畫記憶體區塊:
// 讀 (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
就表明是一個只讀的整型資料; - 再讀取到
*
,由於是在資料定義中存在,即表明是指標型別資料, - 即 const
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 個
*
,那麼要考慮的就是 ( 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 ?
複製程式碼
如果對上面的內容有疑惑,那麼就動手畫畫記憶體圖~~吧 !!!