10

当我在 MS VS C++ 2010 上运行此代码时:

#include <iostream>

int main() {
    const int a = 10;
    const int *b = &a;
    int *c = (int *)b;
    *c = 10000;
    std::cout << c << " " << &a << std::endl;
    std::cout << *c << " " << a << " " << *(&a) << std::endl;
    return 0;
}

输出是:

0037F784 0037F784
10000 10 10

编写该代码的动机是 Stroustrup 的“C++ 编程语言”中的这句话:“可以通过显式类型转换显式删除对指向 const 的指针的限制”。

我知道尝试修改常量在概念上是错误的,但我发现这个结果很奇怪。任何人都可以解释其背后的原因吗?

4

2 回答 2

7

让我们从显而易见的开始:其中一些是平台和编译器相关的。

对于初学者,请参阅这篇关于Explicit Type Conversion的文章,特别是:

指向类型对象的指针const可以转换为指向非const类型的指针。结果指针将引用原始对象。一个类型的对象const或一个类型的对象的引用 const可以转换为一个非const类型的引用。结果引用将引用原始对象。尝试通过此类指针或引用修改该对象的结果将导致寻址异常或与原始指针或引用引用非常量对象相同。是否发生寻址异常取决于实现。

因此,这解释了为什么它可以让您修改变量而不会发牢骚。

请注意,您可以直接使用强制转换运算符来实现相同的目的,因为这就是编译器将为您执行的操作,如本文关于强制转换运算符的说明,并给出了它们的优先顺序。

然而,这里真正的诀窍在于内存模型。像 const int a 这样的静态分配变量实际上可能在内存中永远不会有任何“物理”位置,而只是在编译时被替换。(我试图将手指放在实际参考上,但到目前为止,我能抓住的最接近和最好的是这个(非常好)所以答案为从未使用过的静态变量分配内存? -如果有人找到实际参考,请告诉我们。)

所以这里编译器只是在取笑你,并试图尽可能多地理解你的指针算术,但最终用a实际值代替你第二次cout调用的最后两个部分。

于 2012-11-08T23:48:49.383 回答
0

原因是这是未定义的行为。

Stroustrup 引用可能是指未声明对象const但您只有一个const指向它的指针的情况。

即这是定义明确的(使用有问题的 c 样式转换):

int a{10};
const int* pa = &a;

int* b = (int*)pa;
*b = 5;

这是未定义的:

const int a{10};
const int* pa = &a;

int* b = (int*)pa;
*b = 5;

尝试修改声明的对象const,但是你得到一个指向它的非常量指针,是 UB。

于 2012-11-09T02:43:28.420 回答