为什么编译器允许你在这里“写”一个const变量?

时间:2010-11-16 23:03:19

标签: c++ c pointers const

为什么你可以这样欺骗编译器:

const int a = 5;
*((int*)&a)=5;   // VC/armcc does not complain

当上述内容被“删节”时相当于:

const int *ptr2const = &a;
int *ptr = ptr2const;      // as expected error is raised here
*ptr = 5;

7 个答案:

答案 0 :(得分:9)

Casting是告诉编译器“我知道我在做什么”的方式,所以它不会抱怨。不幸的是,在这种情况下,您将调用未定义的行为。

答案 1 :(得分:7)

C风格的强制转换允许你像你的例子一样抛弃常量。在C ++中,您通常会使用新的样式转换,例如static_cast<>,它们不允许您丢弃constness。只有const_cast<>允许您这样做。

答案 2 :(得分:4)

相当于第二个片段的第二行

int *ptr = ptr2const;      // as expected error is raised here

应该写成

int *ptr = (int *)ptr2const;

答案 3 :(得分:2)

因为C为了获得很大的速度而抛弃了很多类型的安全性。它不能阻止你做错误的事情。它可能会试图警告您,您正在做错误的事情,但如果这是您的目标,您可以始终解决编译器问题。

答案 4 :(得分:2)

将你的常量转换为字符串你可能会发现虽然编译器会让你抛弃const(尽管它可能是不可取的),但链接器可能会放置常量字符串在只读内存中导致运行时崩溃。

答案 5 :(得分:1)

C样式的强制类型转换(例如(int*))在抛弃const的能力上等同于C ++ const_cast,所以你可以通过使用它们来支持const-correctness,尽管这种用法是未经推荐的(可能导致未定义的行为)。

int main()
{
    const int x = 1;
    (int&)x = 2;
    std::cout << x << std::endl;
}

在我的系统上,上面将1写入stdout。您可能会遇到不同的行为。

另一方面......

void foo(const int& x)
{
    (int&)x = 2;
}

int main()
{
    int x = 1;
    foo(x);
    std::cout << x << std::endl;
}

这为我写2。不同之处在于const中使用的foo是const作为类型限定符,而在第一个示例中的main中它被用作存储类。使用const作为存储类声明变量是否并不总是很容易,因此最好不要依赖const_cast或C样式转换来抛弃const。

最好只在大多数情况下使用static_cast,因为这会在编译时以编译错误的形式警告您任何可疑行为。

答案 6 :(得分:0)

这只是“有效”,因为变量是本地的,并且实现无法在一般的本地(自动)变量上强制执行常量(至少在变量的地址不被采用时)。但就语言规范而言,这是未定义行为的范畴。

如果你在全局/静态变量上尝试这个,你会很快发现大多数实现可以执行强制执行const只读存储器,可以在程序的多个实例之间共享。