5

我在我的代码中写了一些类似的东西

const int x=1;
int *ptr;
ptr = &x;
*ptr = 2;

这适用于所有编译器吗?为什么 GCC 编译器没有注意到我们正在更改一个常量变量?

4

4 回答 4

9

const实际上并不意味着“恒定”。C 语言中的“常量”具有在编译时确定的值;文字42就是一个例子。const关键字实际上意味着只读。例如,考虑:

const int r = rand();

的值r直到程序执行时才确定,但关键字表示初始化后const不允许修改。r

在您的代码中:

const int x=1;
int *ptr;
ptr = &x;
*ptr = 2;

赋值ptr = &x;违反约束的,这意味着需要一个符合要求的编译器来抱怨它;您不能合法地将const int*(指向 const int 的指针)值分配给非常量int*对象。如果编译器生成一个可执行文件(它不需要这样做;它可以拒绝它),那么该行为不是由 C 标准定义的。

例如,生成的代码实际上可能将值存储2x-- 但随后对的引用x可能会产生该值1,因为编译器知道在其初始化之后x不能修改。它知道,因为你告诉它,通过定义xconst。如果你对编译器撒谎,后果可能是任意糟糕的。

实际上,可能发生的最糟糕的事情是程序的行为与您期望的一样;这意味着您有一个很难检测到的错误。(但你应该得到的诊断将是一个很大的线索。)

于 2013-03-22T17:33:34.787 回答
4

在线 C 2011 草案

6.7.3 类型限定符

...
6如果尝试通过使用具有非 const 限定类型的左值来修改使用 const 限定类型定义的对象,则行为未定义。如果尝试通过使用具有非易失性限定类型的左值来引用使用易失性限定类型定义的对象,则该行为是未定义的。133)
133) 这适用于那些表现得好像它们是用限定类型定义的对象,即使它们实际上从未定义为程序中的对象(例如内存映射输入/输出地址处的对象)。

重点补充。

由于行为未定义,编译器不需要发出诊断,也不需要停止翻译。这在一般情况下很难捕捉到;假设你有一个像

void foo( int *p ) { *p = ...; }

在它自己的单独翻译单元中定义。在翻译期间,编译器无法知道是否p可以指向const-qualified 对象。如果您的电话类似于

const int x;
foo( &x );

可能会parameter 1 of 'foo' discards qualifiers收到类似或类似 的警告。

另请注意,const限定符并不一定意味着相关变量将存储在只读内存中,因此上述代码可能会“工作”(更新 中的值x),因为您可以x通过结束成功更新-绕着const语义跑。但是,您还不如不声明xconst.

于 2013-03-22T21:14:22.880 回答
0

糟糕的程序员。没有月饼!

如果您需要修改 const,请将其复制到非 const 变量,然后使用它。这是 const 是有原因的。试图“偷偷摸摸”绕过 const 可能会导致严重的运行时问题。即优化器可能使用了内联值等。

const int x=1;
int non_const_x = x;
non_const_x = 2;
于 2013-03-22T17:04:22.787 回答
-1

这里有一个很好的讨论:邪恶的演员是否被邪恶的编译器打败了?

我希望 gcc 编译它,因为:

  • ptr 被允许指向 x,否则阅读它是不可能的,尽管正如下面的评论所说,它不是完全出色的代码,编译器应该抱怨。警告选项(我猜)会影响它是否实际被警告。
  • 当您写入 x 时,编译器不再“知道”它正在写入 const,所有这些都在 C 中的编码人员手中。但是,它确实知道,因此它可能会警告您,具体取决于警告选项你已经选择了。

但是,它是否有效将取决于代码的编译方式、针对所选编译选项实现 const 的方式以及目标 CPU 和体系结构。它可能会起作用。或者它可能会崩溃。或者您可能会写入“随机”内存并导致(或不)一些怪异的效果。这不是一个好的编码策略,但这不是你的问题:-)

于 2013-03-22T17:06:02.590 回答