• C++中如何修改const变量


     

    一、结论

    声明:不同于C语言的const变量修改问题(可以通过指针间接修改const变量的值),这里只讨论C++ 里的const。

    C++ const 修饰符,表示常量,即如果以后保证不会修改则声明为const,否则若要修改,那一开始为什么还要声明为const呢?

    根据C++标准,对于修改const变量,属于:未定义行为(指行为不可预测的计算机代码),这样一来此行为取决于各种编译器的具体实现(即不同编译器可能表现不同)。

    故结论就是:不建议这么做!

    但是,是的,但是,网上论坛、博客里均有有关如何修改const变量的方法,其不是依赖于某种具体的编译器,就是讲的欠考虑。

    方法是在定义变量的时候加上volatile关键字(没有其他方法了吗(比如,const_cast ...)? 是的,目前为止,我只知道这种方法是可能的):

    [cpp] view plain copy
     
     print?
    1. const volatile int i = 10;  

    :关于volatile这里不细讲,详见:volatile 关键字。考虑到volatile的重要性,后面自己也会写一篇关于volatile详解的文章。

    二、分析

    为了说明问题,下面在三种编译器环境下做几个小实验

    1. g++ 

    [cpp] view plain copy
     
     print?
    1. #include <stdio.h>  
    2. int main()  
    3. {  
    4.     const volatile int i = 10;  
    5.     int* pi = (int*)(&i);  
    6.     *pi = 100;  
    7.     printf("*pi: %d ",*pi);  
    8.     printf("i: %d ",i);  
    9.     printf("pi: %p ",pi);  
    10.     printf("&i: %p ", &i);  
    11.     return 0;  
    12. }  

    输出结果:

    gdb查看其汇编代码命令:进入gdb,然后输入:disass main):

    可以看出:输入*pi 和 i 时均是从堆栈(即内存)中取数的。

    反例:把 volatile关键字去掉:

    [cpp] view plain copy
     
     print?
    1. #include <stdio.h>  
    2. int main()  
    3. {  
    4.     const int i = 10;  
    5.     int* pi = (int*)(&i);  
    6.     *pi = 100;  
    7.     printf("*pi: %d ",*pi);  
    8.     printf("i: %d ",i);  
    9.     printf("pi: %p ",pi);  
    10.     printf("&i: %p ", &i);  
    11.     return 0;  
    12. }  

    输出结果:

    由此可见:在没有volatile关键字修饰时,const 变量 i 的值时没有改变的。

    运用gdb查看其汇编代码:

    注意此时(没有加volatile修饰符),输出 变量 i 的值时直接将 0xa(10)值(从符号表中取出的)输出,即此处编译器进行了优化,没有从内存中读。

    注意到:指针 pi 和 &i(i 的地址)值却是一样的。So ,Why?

    这就是C++中的常量折叠:指const变量(即常量)值放在编译器的符号表中,计算时编译器直接从表中取值,省去了访问内存的时间,从而达到了优化。

    而在此基础上加上volatile修改符,即告诉编译器该变量属于易变的,不要对此句进行优化,每次计算时要去内存中取数。

    这里也有个小细节:每种编译器对volatile修饰符的修饰作用效果不一致,有的就直接“不理会”,如VC++6.0编译器(下面会讲到)。

    2. dev c++

    运行结果与1(g++)一致。

    3. VC++ 6.0

    (1)添加volatile修饰符时,输出结果(程序代码同上):

     i 的值还是10,没有改变!这是为什么呢?不急,先看下其汇编代码:

    注意:g++ 汇编代码的mov指令 与 VC++ 6.0的mov指令不同(传送方向相反)。

    真相大白:虽然定义const变量的同时加上了volatile修饰符,但VC++ 6.0编译器还是进行了优化措施,输出 i 时 从编译器的符号表中取值,直接输出。

    (2)无 volatile 修饰符时。输出结果:

    i 的值没有改变,预期中。其汇编代码为:

    结果与添加volatile时相同。

    即在VC++6.0编译环境下,在const变量定义时添加volatile修饰符,与不添加效果是一样的。编译器都采取了优化(甚至把编译器优化选项关闭还是如此,有点恐怖...)。

    4. VS 2010

    再看下Microsoft编译器家族的高级版本:

    (1)添加 volatile 修饰符时,输出结果:

     i 的值被成功修改了!

    (2)无 volatile 修饰符时,输出结果:

    i 的值没有被修改。

    故:不建议修改const变量的值,即使修改也要熟悉当前使用的编译器对于该 未定义行为 是如何解释的

  • 相关阅读:
    Flink 1.14 新特性预览
    基于 MaxCompute 的实时数据处理实践
    Serverless 工程实践 | 细数 Serverless 的配套服务
    不得不看!虚拟货币和区块链的关系
    美国华尔街拥抱区块链是最大的威胁
    1.图片底部圆弧
    二、快捷键
    三、ASP.NET Core 部署Linux
    一、.NET Core MVC 项目结构模板
    一、纯css实现顶部进度条随滚动条滚动
  • 原文地址:https://www.cnblogs.com/lvdongjie/p/5249729.html
Copyright © 2020-2023  润新知