【问题标题】:ANCI C (C90): Can const be changed?ANCI C (C90):可以更改 const 吗?
【发布时间】:2013-09-12 08:43:45
【问题描述】:

我很困惑 ANSI 规范中关于更改声明为 const 的变量可以通过其地址合法修改的内容。不幸的是,我无权访问 C90 规范,但指针冲突:

  1. 关键字 const 不会将变量转换为常量!带有 const 的符号 限定符仅表示该符号不能用于赋值。这使得价值 通过该符号只读;它不会阻止值被修改 其他一些方法是程序内部(甚至外部)的。它非常有用 用于限定指针参数,以表明此函数不会更改参数指向的数据,但其他函数可以。 (专家 C 编程:深度 C 秘密:Peter van der Linden)

  2. 如果尝试通过使用具有非 const 限定类型的左值来修改使用 const 限定类型定义的对象,则行为未定义。如果尝试通过使用具有非 volatile 限定类型的左值来引用使用 volatile 限定类型定义的对象,则行为未定义。 (http://flash-gordon.me.uk/ansi.c.txt)

我在 C99 规范 (n1256.pdf) 中看到了后者。

谁能澄清一下以上两种观点哪个是正确的?

编辑:Expect C Programming 实际上给出了一个示例来演示使用指针更改const 变量的能力。

【问题讨论】:

  • 为什么它们不能都是真的?第一个是说 const 变量可以通过外部方式进行更改。第二个是说,据我所知,故意这样做是未定义的行为。
  • 语言结构旨在防止意外误用,而不是故意误用 - 我试图在这个问题中说服人们同样的事情 - stackoverflow.com/questions/18755713/… - 大多数人似乎认为可以使用语言结构加强安全性或防止故意滥用。值得庆幸的是,该 Q 现在已被关闭,因为另一个问题的答案更合理。

标签: c constants ansi c89


【解决方案1】:

不知道 C90,但 C11 包含这个子句,我想它从第一天(C11,6.7.3/6)就已经存在:

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

volatile 限定的对象也是如此。

【讨论】:

  • 是的,它似乎从一开始就存在。但我想知道这么好的书怎么能把它放在标题下,阅读 ANSI C 标准以获得乐趣、快乐和利润
  • @user926918:您在问题中引用的两个引号没有矛盾,所以我不确定我是否理解您的评论。
【解决方案2】:

在 C90(C89) 中与 C99 类似。

C89 §3.5.3 Type qualifiers

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

未定义的行为并不意味着 C 完全禁止它,只是行为未定义。所以实际上你的两个陈述都是正确的。

【讨论】:

  • 就 C 标准而言,任何触发未定义行为的程序完全没有意义。编译器供应商可以为 C 标准未定义含义的程序分配含义,但我认为某些构造产生未定义行为的声明等同于在文档没有定义的任何编译器上禁止它们的声明明确规定。
  • @supercat 要编写正确且可移植的代码,应始终避免未定义的行为。但是有时无法使用可移植代码来解决特定问题。例如,在某些嵌入式环境中,需要访问地址0。在可移植代码中,这样做是非法的,但编译器可能允许您在这个特定平台上这样做。
  • 如果编译器的文档说明如果代码访问(int*)0 会发生什么,那么在使用该编译器时是不被禁止的。我的观点是,如果标准说一个构造产生未定义的行为,而一个编译器没有为它指定一种行为,那么如果一个人希望编写有意义的代码,就必须认为这种构造是被禁止的。如果在当前调用未定义行为的许多情况下,标准委员会要么指定一组可能发生的有限事情,要么至少指定一个编译器会产生......
  • ...可能发生的“预期”范围之外的行为必须记录[例如指定没有相反的文档,整数溢出将产生未指定的不确定值,该值可能超出类型的正常范围,如果重复读取可能会改变,并且可能是陷阱表示,但可能除非编译器文档指定特定效果或明确说明该行为完全未定义,否则不会产生任何其他效果。这样的规则在交叉检查多个计算结果的某些情况下会很有帮助......
  • ...会显示一个是假的,但是在所有的执行完成之前,代码不会知道哪个计算是假的。在结果无关紧要的计算中避免整数溢出可能会增加代码复杂性而没有真正的好处。
【解决方案3】:

const 变量的更改可以使用指针进行,因为它只是一个内存位置,因此它肯定会接受指针方法所做的更改。

但由于此变量被定义为const,因此其值的更改将调用undefined behaviour

【讨论】:

    【解决方案4】:

    什么声明如:

    T const *p; //p has type “pointer to const T
    

    是什么意思?
    程序可以使用表达式 p 更改 p 指定的指针对象的值,但不能使用表达式 *p 更改 *p 可能指定的任何对象的值。如果程序有另一个非限定类型的表达式e 指定*p 也指定的对象,则程序仍然可以使用e 来更改该对象。 因此,程序可能能够直接从 const-qualified 表达式下更改对象。

    【讨论】:

      【解决方案5】:

      如果您尝试修改 const 变量,则行为未定义是合乎逻辑的。考虑将代码 + 常量放在 ROM 中的嵌入式平台。在这种情况下,根本不可能更改该值,因为它会永远被烧毁。就好像一切都驻留在 RAM 中一样,它很可能是可变的。这就是标准在这种情况下说“未定义行为”的原因 - 行为必须依赖于平台和编译器。

      语句 1 和 2 并不是真正相互排斥的。

      【讨论】:

        【解决方案6】:

        是什么让您觉得这两种说法相互排斥?

        const 只是一个类型限定符,没什么神奇的。

        内存仍然可以通过外部方式改变,或者通过绕过编译器识别类型限制的能力。第二个陈述只是说尝试这样做会产生不确定的结果。它可能会也可能不会改变值。

        在我看来,这两种说法都没有矛盾。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-07-04
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多