在C中,常量变量可以通过指针进行修改吗



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

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

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

const实际上并不意味着"常数"。C中"常量"的某个值是在编译时确定的;文字CCD_ 2是一个例子。const关键字实际上意味着只读。例如:

const int r = rand();

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

在您的代码中:

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

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

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

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

在线C 2011草案:

6.7.3型式鉴定


6如果试图通过使用修改用常量限定类型定义的对象对于具有非常量限定类型的左值,行为是未定义的。如果尝试通过使用左值来指代具有可变定性类型的对象对于非挥发性限定类型,行为是不确定的133(
133(这适用于那些表现得像是用限定类型定义的对象,即使它们是从未实际定义为程序中的对象(例如内存映射输入/输出处的对象地址(

增加了重点。

由于行为未定义,因此编译器不需要发出诊断,也不需要停止翻译。这在一般情况下很难抓住;假设你有一个类似的功能

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

在它自己的独立翻译单元中定义。在转换过程中,编译器无法知道p是否指向const限定的对象。如果您的呼叫类似

const int x;
foo( &x );

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

还要注意,420限定符并不一定意味着关联的变量将存储在只读内存中,因此上面的代码可能会"工作"(更新x中的值(,因为您可以通过围绕const语义进行最终运行来成功更新x。但是,您也可以不将x声明为const

这里有一个很好的讨论:邪恶的编译器会战胜邪恶的铸造吗?

我希望gcc编译这个,因为:

  • ptr可以指向x,否则就不可能读取它,尽管正如下面的评论所说,这不是一个非常出色的代码,编译器应该抱怨。警告选项将(我想(影响它是否真的被警告
  • 当你向x写入时,编译器不再"知道"它正在向const写入,所有这些都掌握在C中的程序员手中。然而,它确实知道,所以它很可能会警告你,这取决于你选择的警告选项

然而,它是否有效将取决于代码的编译方式、所选编译选项的const实现方式以及目标CPU和体系结构。它可能会起作用。否则它可能会崩溃。或者你可能会写一些"随机"的记忆,引起(或不引起(一些奇怪的效果。这不是一个好的编码策略,但这不是你的问题:-(

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

如果你需要修改一个常量,把它复制到一个非常量变量中,然后使用它。它是常量是有原因的。试图"偷偷"绕过const可能会导致严重的运行时问题。即优化器可能使用了内联值等。

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

最新更新