通过非const指针修改const变量

14

以下代码让我有点困惑:


const int e = 2;

int* w = ( int* ) &e;          // (1) cast to remove const-ness
*w = 5;                        // (2)

cout << *w << endl;            // (3) outputs 5
cout << e << endl;             // (4) outputs 2

cout << "w = " << w << endl;   // (5) w points to the address of e
cout << "&e = " << &e << endl;

在(1)中,w指向e的地址。在(2)中,该值被更改为5。然而,当*w和e的值被显示时,它们的值是不同的。但是如果你打印w指针和&e的值,它们具有相同的值/地址。

为什么即使它被更改为5,e仍然包含2?它们存储在一个单独的位置吗?还是一个临时的位置?但是为什么w指向的值仍然是e的地址呢?


15
你现在处于未定义行为领域,任何事情都有可能发生。 - anon
我的头脑太疲惫了,看不出正在发生什么事,但你可以使用const_cast来解决。如果你只是为了了解正在发生的事情而提问,那么你可以忽略此评论。 - erelender
3
你可以使用 const_cast 来实现这个,但它并不会使行为变得更加明确定义。 - CB Bailey
6个回答

17

就像我在评论中所说的,一旦您修改了const值,您就处于未定义的行为领域,因此谈论正在发生什么并没有太多意义。但是到底是怎么回事呢..

cout << *w << endl;            // (3) outputs 5
cout << e << endl;             // (4) outputs 2

猜测是在运行时评估了*w,但将e视为编译时常量。

这是否意味着将const变量转换为非const指针没有任何用处,因为通过该指针进行修改会导致未定义的行为? - jasonline
2
基本上,const 的意义在于你不修改该值... 你永远不应该取消 const。 - Peter Alexander
1
我同意Neil的观点。更详细地说,当X是枚举或整数类型,v是一个整数常量表达式(即const int v = 5;)时,const X c = v定义了一个常量c,它本身可以被用作整数常量表达式。这意味着编译器在编译任何使用c的地方时都可以内联该值,这很可能就是发生的情况。UB是通过const_cast(那里的C样式转换是const_cast)修改该常量可能会做任何事情或什么也不做,包括杀死应用程序。在这种特殊情况下,它正在修改该值。 - David Rodríguez - dribeas
@Gab 好的,我刚刚用 g++ 4.4.1 进行了尝试,没有进行任何优化,结果得到了 5 和 2。这只是表明当你处于 UB 领域时,任何事情都可能发生! - anon
我明白了,感谢您的澄清。我以为我漏掉了什么。 - jasonline
显示剩余6条评论

8

我猜测你可能让编译器出了点问题。它并不希望你对e进行恶意操作,所以当它看到以下这行代码时:

cout << e << endl;

它只是插入值2,而不是查找实际值。您可以通过查看程序的反汇编来验证(或证明)这一点。


4
我猜编译器已经优化了值输出。它认为 e 是常量(理论上不会改变),将 cout << e << endl; 改成了 cout << 2 << endl;。然而,e 仍然需要存在,因为它被 w 使用,所以 w 正确地取出其地址并修改其值,但在 cout 中看不到这一点。
故事的寓意——只有当你真正想要把东西声明为常量时才使用const。强制去除const性质并不是一个好主意。

3
我能想到的唯一可能是编译器以某种方式优化了代码,使得对 e 的任何引用都被替换为 2 的值,即使它分配了内存给 e。
因此,在实际上,注释 (4) 处的行被“优化”为:
cout << "2" << endln;

2

我猜编译器使用常量来优化变量,并将固定值插入到代码中。


1
这在C++14标准的[dcl.type.cv]/4章节中有所涉及(早期标准也有类似的文本):
除了任何声明为mutable的类成员可以被修改外,在对象生命周期内尝试修改const对象会导致未定义行为。
e是一个const对象,*w = 5; 尝试修改该对象,因此结果是未定义行为

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接