使用指针时出现奇怪的行为

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的限制”。我知道尝试修改一个常量在概念上是错误的,但我觉得这个结果相当奇怪。有人能解释一下背后的原因吗?


G++ 4.2.1 做同样的事情。 - Jason
2个回答

7
让我们从显而易见的方面开始:其中一些是平台和编译器相关的。
首先,参阅 显式类型转换中的文章,特别是:
引用一个const类型对象的指针可以转换为非const类型的指针。结果指针将引用原始对象。const类型的对象或引用const类型的对象可以转换为非const类型的引用。结果引用将引用原始对象。试图通过这样的指针或引用修改该对象的结果将导致寻址异常或与引用非常量对象的原始指针或引用相同。是否发生寻址异常取决于实现。
所以,这就解释了为什么它可能让您修改变量而不出错。
请注意,您可以直接使用转换运算符来实现相同的功能,因为编译器会像在转换运算符文章中解释的那样按照它们的优先顺序为您执行。 然而,真正的技巧在于内存模型。像const int a这样的静态分配变量实际上可能从未在内存中拥有任何“物理”位置,并且只是在编译时被替换。 (我正在努力找到实际参考资料,但到目前为止,我能够找到的最接近和最好的是这个(非常好的)SO 回答 to is memory allocated for a static variable that is never used? - 如果有人找到实际参考资料,请告诉我们。) 所以编译器只是尽可能地迎合您的指针算术,并试图使其有些意义,但最终会将实际值替换为第二个cout调用的最后2个部分中的a

实际上,将const int a = 10修改为const int a = x(其中x是我输入的值)是有意义的,这样输出结果为:10000 10000 10000。谢谢! - Kinan Al Sarmini
@KinanAlSarmini:不用谢。很高兴我能帮忙,因为我花了一些时间盯着屏幕才弄清楚。我已经有一段时间没有做过C和C++了,这让我感到失望。所以我很高兴偶尔能涉足这些黑暗的领域。 - haylem
1
+1 对编译器来说只是在哄你 - "愚蠢的人类,为什么你想要做那件事呢?" - Tacroy
@KinanAlSarmini:将来,这种问题可能更适合在StackOverflow而不是P.SE上提问。我认为你会在那里得到更多的答案和更快的回复,这就是为什么我投票支持关闭并转移你的问题的原因。 - haylem

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的对象,但是你得到了一个非常量指针,这是未定义行为。

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