C/C++ Const 小知識

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; 的時候,從左往右讀?。?/p>

  • 先是 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; 的時候,從左往右讀?。?/p>

  • 先是 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; 的時候,從左往右讀?。?/p>

  • 先是 const 表明是只讀的數據類型,
  • int 表明是整型數據,分配 xxx 個字節 ( xxx 根據不同平臺數量不同 ),
  • 這時候 const int 就表明是一個只讀的整型數據;
  • 再讀取到 * ,由于是在數據定義中存在,即表明是指針類型數據,
  • 即 constint *, 表明是一個只讀的整型的指針類型,
  • 再讀取到 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; 的時候,從左往右讀?。?/p>

  • int 表明是整型數據,分配 xxx 個字節 ( xxx 根據不同平臺數量不同 ),
  • 再是 const 表明是只讀的數據類型,
  • 這時候 int const 就表明是一個只讀的整型數據;
  • 再讀取到 * ,由于是在數據定義中存在,即表明是指針類型,
  • int const *, 表明是一個只讀的整型的指針類型數據,
  • 再讀取到 num 表明是一個名為 num 的數據 ,
  • 最后讀取到 ; 表示結束 ( 當然有些語言是沒有 ; 但是有換行,即讀取到換行符,表示結束 ),
  • 那么 int const * num; 最后表示是一個名為 num 且保存只讀的整型的指針數據

第一種和第二種,語義上是一樣的所以結果肯定是一樣的。

[第三種] 首先,編譯器在讀取 int * const num; 的時候,從左往右讀?。?/p>

  • 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; 的時候,從左往右讀?。?/p>

  • 先是 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;
  1. 對于基本類型,只需要考慮自身的內存的讀寫性,一旦加 const 修飾不管放那都是只讀。( 因為從語義上看不管 const 放那它也只有一種結果 )
  2. 對于一級指針類型,考慮的內存有兩塊,一個是自身的內存,一個是指向的內存;const 一旦緊挨著變量名,即自身的內存只讀,其它情況都是指向的內存只讀。
  3. 對于多級指針類型,若有 x 個 * ,那么要考慮的就是 ( x + 1 ) 個內存塊的問題了。
  4. 還有 int constconst 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 ??

如果對上面的內容有疑惑,那么就動手畫畫內存圖~~吧 ?。?!

現在掌握了嗎?

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容