为什么允许使用memcpy使用指向它的指针覆盖const变量?

为什么允许使用memcpy指向它来更改const变量?

这段代码:

const int i=5; int j = 0; memcpy(&j, &i, sizeof(int)); printf("Source: i = %d, dest: j = %d\n", i,j); j = 100; memcpy(&i, &j, sizeof(int)); printf("Source: j = %d, dest: i = %d\n", j,i); return 0; 

编译只是一个警告:

警告:传递’memcpy’的参数1从指针目标类型中丢弃’const’限定符[默认启用]

但确实运行得很好,并改变了const变量的值。

问题问为什么 。 原因如下:

这是允许的,因为一旦你有一个指向内存地址的指针,语言就不知道它指向的是什么。 它可以是变量,结构的一部分,堆或堆栈,或任何东西。 所以它不能阻止你写它。 直接内存访问总是不安全的,如果有另一种方法可以避免。

const停止使用赋值(或增量等)修改const的值。 这种突变是唯一可以保证你无法在const上执行的操作。

另一种看待这种情况的方法是静态上下文(即在编译时)和运行时上下文的划分。 当你编译一段代码时,例如,可以对变量进行赋值,语言可以说“那是不允许的,它是const”,这是一个编译错误。 在此之后,代码被编译成可执行文件,并且它是const的事实丢失了。 变量声明(以及语言的其余部分)作为输入写入编译器。 编译完成后,代码就无关紧要了。 你可以在编译器中做一个逻辑certificate,说const不会改变。 编译的程序运行,我们在编译时知道我们已经创建了一个不违反规则的程序。

引入指针时,您可以在运行时定义行为。 您编写的代码现在无关紧要,您可以[尝试]执行您想要的操作。 指针键入的事实(允许指针算术,将指针末尾的内存解释为特定类型)意味着该语言为您提供了一些帮助,但它不能阻止您做任何事情。 它无法保证,因为您可以将指针指向任何位置。 编译器无法阻止您在运行时使用指针代码破坏规则。

也就是说,指针是我们获得动态行为和数据结构的方式,除了最琐碎的代码之外,它们都是必需的。

(以上内容有很多警告,即代码启发式,更复杂的静态分析总线广泛适用于vanilla编译器。)

尝试修改const限定变量的值会导致C中的未定义行为。您不应该依赖于您的结果,因为任何事情都可能发生。

C11(n1570),§6.7.3类型限定词

如果尝试通过使用具有非const限定类型的左值来修改使用const限定类型定义的对象,则行为未定义。

没有任何东西强迫编译器生成诊断消息

实际上,这个限定符对机器代码没有太大影响。 const限定变量通常不驻留在只读数据段中(显然,不在您的实现中,尽管在另一个上可能不同)。

编译器无法轻易告诉指针在给定函数中指向的内容。 一些静态分析工具可以执行指针分析 。 但是,它很难实现,将它置于标准中是愚蠢的。

原因是因为C语言允许任何指针类型隐式地转换为类型void* 。 它的设计方式是因为void指针用于generics编程。

因此,即使程序在这种情况下调用未定义的行为,也不允许C编译器停止编译。 但是,只要您隐式尝试抛弃const限定符,一个好的编译器就会发出警告。

C ++比C具有“更强的类型”,这意味着它需要显式转换指针类型才能编译此代码。 这是C ++实际修复的C语言的一个缺陷。

虽然“正式”它在现实中尚未定义,但它已经定义了很多 – 您将更改const变量的值。 这提出了为什么它是const开始的问题。