在C中,可以通过指针修改const变量吗?

时间:2013-03-22 17:01:55

标签: c pointers const

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

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

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

4 个答案:

答案 0 :(得分: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的指针)值分配给非const int*对象。 如果编译器生成一个可执行文件(它不需要这样做;它可能只是拒绝它),那么行为不是由C标准定义的。

例如,生成的代码实际上可能会在2中存储值x - 但稍后对x的引用可能会产生值1,因为编译器知道 x在初始化后无法修改。它知道,因为你这么说,将x定义为const。如果你撒谎到编译器,后果可能是任意的坏。

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

答案 1 :(得分:4)

Online C 2011 draft

6.7.3类型资格

...
6 如果尝试通过使用修改使用const-quali fi ed类型定义的对象 具有非const-quali fi ed类型的左值,行为未定义。如果是尝试 通过使用左值来指代用挥发性限定类型定义的对象 对于非易失性合格类型,行为未定义。 133)
133)这适用于那些表现得好像被定义为合格类型的对象,即使它们是 从来没有真正定义为程序中的对象(例如内存映射输入/输出中的对象) 地址)。

重点补充。

由于行为未定义,因此编译器不需要发出诊断,也不需要停止转换。在一般情况下,这很难理解;假设你有像

这样的功能
void foo( int *p ) { *p = ...; }

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

const int x;
foo( &x );

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

另请注意,const限定符并不一定意味着关联变量将存储在只读内存中,因此上述代码可能会“起作用”(更新{{1}中的值) })你通过围绕x语义进行最终运行来成功更新x。但是,您也可以不将const声明为x

答案 2 :(得分:0)

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

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

const int x=1;
int non_const_x = x;
non_const_x = 2;

答案 3 :(得分:-1)

这里有一个很好的讨论:Does the evil cast get trumped by the evil compiler?

我希望gcc能够编译它,因为:

  • ptr被允许指向x,否则读取它是不可能的,尽管如下面的评论所说它不是很精彩的代码并且编译器应该抱怨。警告选项(我猜)会影响它是否真的被警告过。
  • 当您写入x时,编译器不再“知道”它正在写入const,所有这些都在编码器的C中。但是,它确实知道,因此它可能会警告您,具体取决于您选择的警告选项。
然而,

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