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

为什么允许使用memcpy的指针覆盖常量变量?,c,constants,memcpy,C,Constants,Memcpy,为什么允许通过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使用指向常量变量的指针来更改常量变量

此代码:

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中出现未定义的行为。您不应该依赖您的结果,因为任何事情都可能发生

C11(n1570),§6.7.3类型限定符 如果试图通过使用修改使用常量限定类型定义的对象 对于具有非常量限定类型的左值,行为未定义

不强制编译器生成诊断消息

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

编译器不能轻易地分辨给定函数中的指针指向什么。可以使用一些静态分析工具来执行。但是,它很难实现,如果将它放在标准中,那将是愚蠢的。

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

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

const
停止您使用赋值(或增量等)修改
const
的值。这种变异是唯一能保证你不能在常量上执行的操作

另一种方法是划分静态上下文(即在编译时)和运行时上下文。例如,当您编译一段代码时,可能会对一个变量进行赋值,语言可能会说“这是不允许的,它是常量”,这是一个编译错误。在此之后,代码被编译成一个可执行文件,它是一个
const
的事实将丢失。变量声明(以及语言的其他部分)作为输入写入编译器。一旦编译完成,代码就不相关了。您可以在编译器中进行逻辑证明,说明
const
s没有更改。编译后的程序运行,我们在编译时知道我们已经创建了一个不违反规则的程序

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

也就是说,指针是我们获取动态行为和数据结构的方式,除了最简单的代码外,其他所有代码都需要指针


(上面有很多警告,即代码启发式,更复杂的静态分析总线对普通编译器来说基本上是正确的。)

虽然“正式”它是未定义的,但实际上它定义得非常明确-您将更改const变量的值。这就提出了一个问题,为什么它是常量。

原因是C语言允许将任何指针类型隐式转换到类型
void*
。它是这样设计的,因为void指针用于泛型编程

因此,不允许C编译器停止代码的编译,即使程序在这种情况下调用未定义的行为。然而,一个好的编译器会在您隐式地试图丢弃常量限定符时发出警告


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

const并不像你希望的那样强大。C不能保证可变的只能是“只读”。当然。但是这个问题是C语言指定的是C,而不是C++。这不回答这个问题。乔:为什么允许重写const变量?因为没有什么强迫编译器生成诊断消息。或者你是在说为什么这个标准是这样的?嗯。我只是觉得这并不是对“为什么”的一个直接回答,更多的是对事实的评论。这不是问题,但在我看来,这并不是一个答案。@Joe:我补充了一个关于原因的小摘录。希望它现在看起来像一个真正的答案(顺便说一句,我的英语确实很差)。