3

为什么允许使用 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 变量的值。

4

4 回答 4

7

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

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

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

没有什么可以强制编译器生成诊断消息

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

编译器无法轻易判断给定函数中指针指向的内容。使用一些执行指针分析的静态分析工具是可能的。但是,实施起来很困难,把它放在标准中是愚蠢的。

于 2013-03-20T13:06:31.357 回答
7

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

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

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

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

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

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

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

于 2013-03-20T13:07:44.443 回答
1

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

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

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

于 2013-03-20T13:47:25.710 回答
-1

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

于 2013-03-20T13:33:31.913 回答