const强制转换为全局变量,程序崩溃(c++)

const cast to a global var and program crashed (C++)

本文关键字:崩溃 程序 c++ 全局变量 转换 const      更新时间:2023-10-16
int main()
{
    const int maxint=100;//The program will crash if this line is put outside the main
    int &msg=const_cast<int&>(maxint);  
    msg=200;  
    cout<<"max:"<<msg<<endl; 
    return 0;
}

如果将'const int maxint=100;'定义放在主函数中,则函数将运行正常,但如果放在主函数中,则会崩溃并弹出"Access Violation"错误消息。

有人说这是一种"未定义的行为",我想知道确切的答案,以及如何安全地使用const强制转换?

他们是正确的,这是未定义的行为。您不允许修改const变量的值,这是抛弃某些东西的const性的危险:您最好知道它不是真正的const

编译器,看到maxintconst,不应该修改,甚至不需要给它一个地址。如果它认为合适,它可以用100取代maxint的所有用途。正如Matteo Italia指出的那样,它也可能只是将常量放入内存的只读部分,这可能就是你正在发生的事情。这就是为什么修改它会产生未定义的行为。

可以安全地丢弃变量的const属性的唯一方法是,该变量实际上不是const,而是将const限定符添加到非const变量中,如:

int f(const int& x) {
    int& nonconst = const_cast<int&>(x);
    ++nonconst;
}
int blah = 523;
f(blah); // this is safe
const int constblah = 123;
f(constblah); // this is undefined behaviour

考虑这个示例,它可以完美地编译:

int f(const int& x) {
    int& nonconst = const_cast<int&>(x);
    ++nonconst;
}
int main() {
    f(4); // incrementing a number literal???
}

你可以看到使用const_cast是非常危险的,因为实际上没有办法判断一个变量最初是否为const。您应该尽可能避免使用const_cast(对于函数,通过不接受const参数)。

修改为const的对象(可变成员除外)会导致未定义的行为(来自c++ 03标准):

7.1.5.1/4 "The cv-qualifiers"

除了任何被声明为可变的类成员(7.1.1)都可以被修改之外,在const对象的生命周期(3.8)内,任何修改const对象的尝试都会导致结果在未定义行为中。

以上未定义的行为在const_cast:

的标准章节中被特别调用:

5.2.11/7 "Const cast"

[注:根据对象的类型,写操作通过指针、左值或指向数据成员的指针转换const-qualifier68)的Const_cast可能会产生undefined行为(7.1.5.1)。)

所以,如果你有一个const指针或引用指向一个实际上不是const的对象,你可以写这个对象(通过抛掉常量),但如果对象实际上是const,则不允许。

例如,编译器允许将const对象放在只读存储器中。但是,它并不一定要这样做,而且对于不会崩溃的测试代码来说,显然不需要这样做。

只允许丢弃已知而不是const的对象的constness。例如,一个接口可能通过使用const指针或const引用传递对象,但是你传入的对象不是const,并且想要/需要修改它。在这种情况下,抛弃constness可能是正确的。

另一方面,抛弃一个一直是const的对象的constness可能会让你陷入深深的麻烦:当访问这个对象时,特别是当向它写东西时,系统可能会导致各种奇怪的事情:行为没有定义(由c++标准),在特定的系统上,它可能会导致访问冲突(因为对象的地址被安排在只读区域)。

请注意,尽管有另一个响应,我看到const对象需要获得一个地址分配,如果地址曾经以某种方式被占用和使用。在代码中,const_cast<int&>(maxint)表达式本质上获得常量int的地址,该常量显然存储在标记为只读的内存区域中。您的代码片段的有趣之处在于,它就像显然工作,特别是在打开优化时:代码足够简单,编译器可以告诉更改的位置并没有真正使用,并且实际上没有尝试更改内存位置!在这种情况下,不会报告访问违规。这显然是在函数内部声明常量时的情况(尽管常量也可能位于通常不能标记为只读的堆栈上)。代码的另一个潜在结果是(与是否在函数内声明常量无关),它实际上被更改,有时读取为100,而在其他上下文中(以某种方式或另一种方式涉及地址)读取为200。