美文网首页
C/C++ Const 小知识

C/C++ Const 小知识

作者: 半纸渊 | 来源:发表于2019-03-27 12:59 被阅读0次

    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 就表明是一个只读的整型数据;
    • 再读取到 * ,由于是在数据定义中存在,即表明是指针类型数据,
    • 即 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; 的时候,从左往右读取:

    • 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;
    
    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 🚫
    

    如果对上面的内容有疑惑,那么就动手画画内存图~~吧 !!!

    现在掌握了吗?

    相关文章

      网友评论

          本文标题:C/C++ Const 小知识

          本文链接:https://www.haomeiwen.com/subject/dfhzvqtx.html