一、结论

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

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

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

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

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

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

  1. const volatile int i = 10;

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

二、分析

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

1. g++

  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\n",*pi);
  8. printf("i: %d\n",i);
  9. printf("pi: %p\n",pi);
  10. printf("&i: %p\n", &i);
  11. return 0;
  12. }

输出结果:

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

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

反例:把 volatile关键字去掉:

  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\n",*pi);
  8. printf("i: %d\n",i);
  9. printf("pi: %p\n",pi);
  10. printf("&i: %p\n", &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变量的值,即使修改也要熟悉当前使用的编译器对于该 未定义行为 是如何解释的

C++中如何修改const变量的更多相关文章

  1. 浅谈JS中 var let const 变量声明

    浅谈JS中 var let const 变量声明 用var来声明变量会出现的问题: 1. 允许重复的变量声明:导致数据被覆盖 2. 变量提升:怪异的数据访问.闭包问题 3. 全局变量挂载到全局对象:全 ...

  2. 匿名内部类中不能修改int变量时、final int i 不能改变i的值时、或 i++线程不安全。使用AtomicInteger;

    在匿名内部类或某某情况下中引入的变量必须是Final最终型的:这时还想要去修改这个变量就需要使用到AtomicInteger这个类了: AtomicInteger CarSize = new Atom ...

  3. 修改const变量

    看下面的一段代码 ; int * j=(int*)(&i); // 运行正确,j确为i的地址,但 int *j=&i; 编译错误 *j=; //确实改变了i的值 printf(&quo ...

  4. java中static修改成员变量和函数和其他使用

    一.通过static修饰的成员变量初始化只会初始化一次 //静态变量初始化只会初始化一次 public class zuishuai { public static void main(String[ ...

  5. const变量的存储区及修改权限

    转自const变量的存储区及修改权限 [cpp] view plaincopy const int a = 1; int *p = const_cast<int*>(&a); *p ...

  6. 如何修改const常量值

    总结:这个跟计算机语言类别和编译器有关,本文是在linux环境下说明的. 分两种情况: 1. C语言: 2. C++语言: /*! * \Description: * \author scictor ...

  7. mutable用于修改const成员函数中的成员变量

    http://no001.blog.51cto.com/1142339/389840/ mutalbe的中文意思是“可变的,易变的”,跟constant(既C++中的const)是反义词. 在C++中 ...

  8. c++中的const参数,const变量,const指针,const对象,以及const成员函数

    const 是constant 的缩写,“恒定不变”的意思.被const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性.所以很多C++程序设计书籍建议:“Use const whe ...

  9. js中三种定义变量 const, var, let 的区别

    js中三种定义变量的方式const, var, let的区别 1.const定义的变量不可以修改,而且必须初始化. 1 const b = 2;//正确 2 // const b;//错误,必须初始化 ...

随机推荐

  1. 图片处理 Pillow

    Pillow 在python3下用PIL做图像处理 Python图像处理库:Pillow 初级教程 from PIL import Image im = Image.open('22.gif') pr ...

  2. 【转】Android - Button(按钮)的响应点击事件的4种写法

    原文网址:http://www.yrom.net/blog/2011/12/12/android-4-onclicklistener-of-button/ Button控件setOnclickList ...

  3. 自杀程序&递归删除目录

    自杀程序,删掉自身,包括执行程序所在的文件夹. 仅在windows下进行了测试,使用语言C# string fileName = Path.GetTempPath() + "killmyse ...

  4. 发送一个简单的HTTP GET请求并且取回响应。

    string uri="http//www.baidu.com"; WebClient wc = new WebClient(); Console.WriteLine(" ...

  5. ios游戏开发--cocos2d学习(3)

    ------------继续上一节的内容. “接收触摸事件”: CCLayer类是用来接收触摸输入的.不过你要首先启用这个功能才可以使用它. self.isTouchEnabled = YES;//此 ...

  6. 在MFC框架中使用OpenGL的简单实例

    引言 我们知道,在MFC框架中,用于绘图的接口是GDI.但GDI只能绘制简单的2D图形,要想制作精美的3D图形,一个可行的办法是使用OpenGL或者Direct3D等第三方库. 由于最近在给导师的一个 ...

  7. 【Zookeeper学习】Zookeeper-3.4.6安装部署

    [时间]2014年11月19日 [平台]Centos 6.5 [工具] [软件]jdk-7u67-linux-x64.rpm zookeeper-3.4.6.tar.gz [步骤] 1. 准备条件 ( ...

  8. 洛谷 P1156 垃圾陷阱

    2016-05-31 09:54:03 题目链接 :洛谷 P1156 垃圾陷阱 题目大意: 奶牛掉坑里了,给定坑的深度和方块的个数,每个方块都可以垫脚或者吃掉维持生命(初始为10) 若可以出来,求奶牛 ...

  9. 如何用Java编写一段代码引发内存泄露

    本文来自StackOverflow问答网站的一个热门讨论:如何用Java编写一段会发生内存泄露的代码. Q:刚才我参加了面试,面试官问我如何写出会发生内存泄露的Java代码.这个问题我一点思路都没有, ...

  10. Python解释器

    当我们编写Python代码时,我们得到的是一个包含Python代码的以.py为扩展名的文本文件.要运行代码,就需要Python解释器去执行.py文件. 由于整个Python语言从规范到解释器都是开源的 ...