转载自:https://blog.csdn.net/heyabo/article/details/8745942
一、结论
声明:不同于 C 语言的 const 变量修改问题(可以通过指针间接修改 const 变量的值),这里只讨论 C++ 里的 const。
为什么?因为 gcc(按 c 语法) 和 g++(按 c++ 语法) 有区别,如图: ![gcc 与 g++](https://img-blog.csdn.net/20180519210156384?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1lMRDEw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)C++ const 修饰符,表示常量,即如果以后保证不会修改则声明为const,否则若要修改,那一开始为什么还要声明为 const 呢?
根据 C++ 标准,对于修改 const 变量,属于:未定义行为(指行为不可预测的计算机代码),这样一来此行为取决于各种编译器的具体实现(即不同编译器可能表现不同)。
故结论就是:不建议这么做!
但是,是的,但是,网上论坛、博客里均有有关如何修改 const 变量的方法,其不是依赖于某种具体的编译器,就是讲的欠考虑。
方法是在定义变量的时候加上 volatile 关键字(没有其他方法了吗(比如,const_cast …)? 是的,目前为止,我只知道这种方法是可能的):
const volatile int i = 10;
注:关于 volatile 这里不细讲,详见:volatile 关键字。考虑到 volatile 的重要性,后面自己也会写一篇关于 volatile 详解的文章。
二、分析
为了说明问题,下面在三种编译器环境下做几个小实验
1. g++
#include <stdio.h>
int main()
{
const volatile int i = 10;
int* pi = (int*)(&i);
*pi = 100;
printf("*pi: %d\n",*pi);
printf("i: %d\n",i);
printf("pi: %p\n",pi);
printf("&i: %p\n", &i);
return 0;
}
输出结果:
gdb查看其汇编代码(命令:进入gdb,然后输入:disass main):
可以看出:输入*pi 和 i 时均是从堆栈(即内存)中取数的。
反例:把 volatile关键字去掉:
#include <stdio.h>
int main()
{
const int i = 10;
int* pi = (int*)(&i);
*pi = 100;
printf("*pi: %d\n",*pi);
printf("i: %d\n",i);
printf("pi: %p\n",pi);
printf("&i: %p\n", &i);
return 0;
}
输出结果:
由此可见:在没有 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变量的值,即使修改也要熟悉当前使用的编译器对于该 未定义行为 是如何解释的。
参考文章:
1. change the value of const variable in C++
2. C/C++ changin the value of a const
4. volatile 关键字
</div>