为什么允许使用memcpy指针更改常量变量?
此代码:
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”限定符 [默认启用]
但运行得很好,并更改了常量变量的值。
原因是因为C语言允许将任何指针类型隐式转换到/从类型void*
转换。它是这样设计的,因为void指针用于泛型编程。
所以C编译器不允许停止你的代码编译,即使程序在这种情况下调用了未定义的行为。然而,一个好的编译器会在你试图隐式抛弃const限定符时给出警告。
C语言比C语言具有“更强的类型”,这意味着它需要指针类型的显式转换来编译这段代码。这是C语言的一个缺陷,实际上C已经修复了。
问题问为什么。原因如下:
这是允许的,因为一旦你有了一个指向内存地址的指针,语言就不知道它指向什么。它可以是变量、结构的一部分、堆或堆栈,或者其他任何内容。因此,它无法阻止您写信给它。直接内存访问总是不安全的,如果有另一种方法可以避免。
常量
会阻止您使用赋值(或增量等)修改常量
的值。这种突变是它唯一可以保证您无法在常量上执行的操作。
另一种看待这个问题的方法是静态上下文(即在编译时)和运行时上下文的划分。例如,当您编译一段代码时,例如,可以对变量进行赋值,该语言可以说“这是不允许的,它是const”,这是一个编译错误。在此之后,代码被编译成可执行文件,并且它是一个const
的事实将丢失。变量声明(以及语言的其余部分)作为编译器的输入写入。编译后,代码就不相关了。您可以在编译器中进行逻辑证明,以说明 const
没有更改。编译后的程序运行,我们在编译时知道我们创建了一个不违反规则的程序。
当你引入指针时,你就有了可以在运行时定义的行为。你写的代码现在无关紧要了,你可以做你想做的。指针是类型化的(允许指针算术,将指针末尾的内存解释为特定类型)这一事实意味着该语言给了你一些帮助,但它不能阻止你做任何事情。它不能保证,因为你可以将指针指向任何地方。编译器不能阻止你在运行时用使用指针的代码破坏规则。
也就是说,指针是我们获得动态行为和数据结构的方式,除了最琐碎的代码之外,它对所有代码都是必要的。
(以上受到许多警告的约束,即代码试探法,更复杂的静态分析总线对普通编译器来说基本上是正确的。)
尝试修改const限定变量的值会导致C中的未定义行为。您不应该依赖您的结果,因为任何事情都可能发生。
如果尝试通过使用具有非const限定类型的左值来修改使用const限定类型定义的对象,则行为未定义。
没有强制编译器生成诊断消息。
事实上,这个限定符对机器代码没有太大的影响。const 限定变量通常不驻留在只读数据段中(显然,它不在您的实现中,尽管它在另一个实现上可能有所不同)。
编译器不能很容易地判断指针在给定函数中指向什么。一些执行指针分析的静态分析工具是可能的。但是,它很难实现,把它放在标准中是愚蠢的。
问题内容: 为什么无法覆盖静态方法? 如果可能,请使用示例。 问题答案: 覆盖取决于拥有类的实例。多态性的重点是可以对一个类进行子类化,并且实现那些子类的对象对于在超类中定义的相同方法(在子类中被重写)将具有不同的行为。静态方法未与类的任何实例相关联,因此该概念不适用。 影响Java设计的因素有两个。一个是对性能的关注:Smalltalk批评它太慢(垃圾回收和多态调用是其中的一部分),Java的创
我一直认为像< code>const int *a这样的语句意味着< code>a是指向< code>const数据的< code>int指针,因此不能修改它所指向的值。事实上,如果您< code>const int a [] = {1,2,3}然后发出< code>a[0] = 10,您将会得到编译器错误。 然而,令我惊讶的是,以下编译没有任何警告并且运行良好。 为什么允许这样做?这是因为演员阵
我在Rust中尝试函数指针魔术,最后得到了一个代码片段,我完全无法解释它为什么要编译,甚至无法解释它为什么要运行。 我无法解释为什么调用的方法是在
问题内容: 我在日食中收到很多这样的警告: 变量“ myVariable”必须是私有的,并且具有访问器方法。 我想得到它们是因为我没有在eclipse中手动将protectedAllowed设置为true。但是为什么默认情况下将其设置为false?我不应该使用受保护的属性吗? 问题答案: 从理论上讲,受保护的属性(变量)在面向对象的语言中是一种反模式。如果仅子类需要访问其超类的成员属性,则将属性本
问题内容: 我试图弄清楚为什么我无法覆盖通过隔离范围()传递给angularJS指令的值。我尝试使用以下方法覆盖的值: 但是,由于某些原因,它不起作用。 如果我将其更改为: 有用。另外,在作品上分配价值。 为什么第一种方法不起作用? 我已经创建了这个示例插件。 问题答案: 如您在此处使用的那样,需要通过插值指令从属性获取值。似乎指令首先被加载,然后值被求值。因此,在当前摘要周期中不会发生更改。如果
问题内容: 考虑一下Java中的以下接口: 和以下类: 为什么类A可以出现并覆盖接口I的最终常量? 自己尝试: 问题答案: 尽管事实上您正在隐藏变量,但是知道可以在java中更改final字段非常有趣,因为您可以在此处阅读: Java 5-“最终”不再是最终的 挪威Machina Networks的Narve Saetre昨天给我发了一封便条,其中提到我们可以将句柄更改为最终数组,这是很遗憾的。我