我在我的代码中写了类似的东西
const int x=1;
int *ptr;
ptr = &x;
*ptr = 2;
这适用于所有编译器吗?为什么GCC编译器没有注意到我们正在改变一个常量变量?
答案 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)
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能够编译它,因为:
是否有效将取决于代码的编译方式,为所选编译选项实现const的方式以及目标CPU和体系结构。它可能会奏效。或者它可能会崩溃。或者你可以写一个“随机”的内存,并导致(或不)一些怪异的影响。这不是一个好的编码策略,但这不是你的问题: - )