C++中如何修改const变量 (指针)

一、结论

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

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

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

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

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

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

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变量的值,即使修改也要熟悉当前使用的编译器对于该 未定义行为 是如何解释的

 

5.const全局变量与const局部变量关于修改的问题:

const的变量在特定情况下可以通过指针修改,但是在另一些情况下是不能通过指针修改。

以下是VC6下才测试。

1 不能修改的情况(const全局常量)

 
  1. #include

  2. int const a = 10;

  3. void main(void)

  4. {

  5. int *p = (int*)&a;

  6. *p = 20;

  7. printf("%d\n", *p);

  8. }

 

程序编译通过,但运行时错误:

指示a存储的空间不可以写,也就是没有写权限,不能修改其值。估计是存储在全局空间,且只有可读属性。

 

2 能修改的情况(const局部常量)

 
  1. #include

  2. void main(void)

  3. {

  4. int const a = 10;

  5. int *p = (int*)&a;

  6. *p = 20;

  7. printf("&a=%d\n", &a);

  8. printf(" p=%d\n", p);

  9. printf(" a=%d\n", a);

  10. printf("*p=%d\n", *p);

  11. }

 

程序能正常运行,且常量被修改了,但是有一个问题:

   为什么 printf(" a=%d\n", a);

   打印a=10?

难道一个地址空间可以存储不同的俩个值,当然不能,哈哈,这是因为a是const变量,编译器对a在预处理的时候就进行了替换。编译器只对const变量的值读取一次。所以打印的是10。a实际存储的值发生了改变。但是为什么能改变呢,从其存储地址可以看出来,其存储在堆栈中。

验证如下:

 
  1. #include

  2. void main(void)

  3. {

  4. int const a = 10;

  5. int b = 20;

  6. int *p = (int*)&a;

  7. *p = 20;

  8. printf("&a=%x\n", &a);

  9. printf("&b=%x\n", &b);

  10. printf(" p=%x\n", p);

  11. printf(" a=%d\n", a);

  12. printf("*p=%d\n", *p);

  13. }

 

变量a和b的地址相近。

总结:

(1)const全局变量存储在全局存储空间,其值只有可读属性,不能修改;

(2)const局部变量存储在堆栈中,可通过指针修改其值;

(3)const变量在预处理是处理,编译器只对其值读取一次。

 

转自:http://blog.csdn.net/heyabo/article/details/8745942

           http://blog.chinaunix.net/uid-26853826-id-3365403.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值