【问题标题】:Why are we allowed to change values of "const" qualified variables?Why pointers are allowed for this,but not assignment?为什么允许我们更改“const”限定变量的值?为什么允许使用指针,但不允许赋值?
【发布时间】:2013-05-08 17:06:15
【问题描述】:

考虑以下 2 个程序 prog1prog2。如果我尝试使用指针 ptr 更改 const 限定变量 i 的值,我收到警告(不是错误)"initialization discards qualifiers from pointer target type|",但程序仍然运行并显示新值。但是如果我尝试使用赋值语句在第二个程序中更改 i 的值,我得到 错误(非警告)assignment of read-only variable 'i'|

以下是由这个前提引起的混淆:

1) 为什么我们可以在任何情况下更改只读const 限定变量的值?这难道不违背使用const 限定符的目的吗?我们试图这样做吗?

2)即使由于某些奇怪的原因我们被允许更改常量的值,为什么要区分使用指针更改只读const 限定变量的值(这是允许的,但有警告)并通过使用赋值操作(这是不允许的并给我们一个错误)?

//prog1
#include <stdio.h>

int main ()
{
 const int i=8;
 int *ptr=&i;
 *ptr=9;
 printf("%d",*ptr);  //Prints new value nevertheless
}

警告:初始化会丢弃来自指针目标类型的限定符|

//prog2
#include <stdio.h>

int main()
{
const int i=8;
i=10;
printf("%d",i);
}

错误:赋值只读变量'i'|

编辑 H2CO3

这里我不止一次改变const限定变量的值。我只得到一个警告,和prog1一样

//prog3
#include <stdio.h>

int main ()
{
const int i=8;
int *ptr=&i;
*ptr=9;
*ptr=10;
printf("%d",*ptr);  //Prints 10
}

【问题讨论】:

  • 更正了 2 个错别字。现在确定。
  • 为什么?因为,主要是由于历史原因(以及不良的实践习惯),C 标准已经如此定义或实施。 (在 C++ 中可能有所不同)。
  • 您没有意识到 prog1 没有违规,您在任何时候都不会更改任何 const 内容。您将指针设置为 const'd 指针,然后更改该指针。试试int const* ptr = &amp;i,然后再试一次。
  • @Shark Prog1 确实有约束违规。
  • 因为 C 编译器信任程序员,不会尝试修复程序员的错误。

标签: c pointers constants assignment-operator


【解决方案1】:

1) 为什么我们可以在任何情况下更改只读const 限定变量的值?这不是违背了使用const 限定符的目的吗?

试图通过赋值运算符改变一个 const 限定的对象是违反约束的:

约束下的6.5.16:

2 赋值运算符应有一个可修改的左值作为其左操作数。

可修改的左值在 6.3.2.1 (1) 中定义:

可修改的左值是没有数组类型、没有不完整类型、没有 const 限定类型的左值,如果是结构或联合,没有任何具有 const 限定类型的成员(递归地包括所有包含的聚合或联合的任何成员或元素)。

作为约束违规,它需要编译器根据 5.1.1.3 (1) 提供诊断消息:

如果预处理翻译单元或翻译单元包含违反任何语法规则或约束的情况,则符合标准的实现应产生至少一个诊断消息(以实现定义的方式标识),即使该行为也明确指定为未定义或实现定义。在其他情况下不需要生成诊断消息。

但不需要实现来拒绝无效程序,因此诊断消息也可能是警告而不是错误。

但是,通过没有 const 限定类型的左值修改声明为 const 的对象不是违反约束,尽管它会调用未定义的行为,6.7.3 (6):

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

由于它既不是违反约束也不是无效语法,它甚至不需要发出诊断消息。

如果我们尝试这样做,难道不应该得到错误吗?

如果您尝试通过具有 const 限定类型的左值修改对象,您必须收到诊断消息。

由于严重违反了声明的意图,大多数编译器在这些情况下都会发出错误。

如果您尝试通过具有非 const 限定类型的左值来修改具有 const 限定类型的对象,如

const int i=8;
int *ptr=&i;
*ptr=9;

尝试通过表达式 *ptr = 9 修改 i 会调用未定义的行为,但不是违反约束(或语法错误),因此不需要诊断消息(并且没有给出)。

初始化时发出诊断消息

int *ptr = &i;

因为这又违反了约束,根据 6.5.16.1 (1):

应满足以下条件之一:

  • 左操作数具有原子、合格或非限定算术类型,右操作数具有算术类型;
  • 左操作数具有与右操作数兼容的结构或联合类型的原子、限定或非限定版本;
  • 左操作数具有原子、限定或非限定指针类型,并且(考虑左操作数在左值转换后将具有的类型)两个操作数都是指向兼容类型的限定或非限定版本的指针,和指向的类型to by left 具有所有由 right 指向的类型的限定符;
  • 左操作数具有原子、限定或非限定指针类型,并且(考虑左操作数在左值转换后的类型)一个操作数是指向对象类型的指针,另一个是指向限定或非限定类型的指针void 的非限定版本,左边指向的类型具有右边指向的类型的所有限定符;
  • 左边的操作数是一个原子的、合格的或不合格的指针,右边是一个空指针常量;或
  • 左操作数的类型为 atomic、qualified 或 unqualified _Bool,右操作数是指针。

然而,该诊断通常是一个警告而不是错误,因为人们可能会明确地将 const 扔掉,

int *ptr = (int*)&i;

而不能从i 中丢弃const

通过指向非 const 限定对象类型的指针修改对象是有效的,如果指向的对象是可修改。愚蠢的例子:

int i = 8;
const int *cptr = &i;  // valid, no problem adding const
int *mptr = (int*)cptr;
*mptr = 9;             // no problem, pointee is non-const

2) 即使由于某些奇怪的原因我们被允许更改常量的值,为什么要区分使用指针更改只读 const 限定变量的值(这是允许的,但有警告)和通过使用赋值操作(这是不允许的并且给我们一个错误)?

直接分配给具有 const 限定类型的对象不仅违反约束,而且明显违反所述语义。声明对象const 明确表示“我不希望修改该对象”。

通过指向非 const 限定类型的指针修改对象不是违反约束,只有当指针对象具有 const 限定类型时才会发生未定义的行为。允许将指向 const 限定类型的指针转​​换为指向相应非 const 限定类型的指针,并且通过该指针修改指针对象可能是有效的,因此您只会收到警告,并且只有在未进行转换的情况下明确的。

在给定的简短示例中,编译器可以检测到指针对象具有 const 限定类型,因此修改会调用未定义的行为,但通常这很难,而且通常无法检测到。因此,编译器甚至不会尝试检测简单的情况,这是不值得的。

【讨论】:

  • 如果您也可以查看以下问题,您会很高兴,也许可以发表评论,因为我不确定它是否值得您回答。 stackoverflow.com/questions/16543795/…
  • 我看到 Eric Postpischil 打败了我。他的回答很全面。
  • 谢谢,现在对 Eric Postpischil 新的尊重!!
【解决方案2】:

为什么我们可以在任何情况下更改只读const 限定变量的值?

我们不是。我不明白你为什么这么认为,也不知道哪个例子表明了这一点。

为什么要区分使用指针更改只读 const 限定变量的值(这是允许的,但有警告)

再次重申:不允许, 因此发出警告。 (警告要认真对待 - 你似乎没有给他们任何意义......)只是编译器不知道指针指向一些const-qualified对象(因为它被声明为非常量 T *)。

至于为什么更改变量有效:

解释 #1:这是未定义的行为(违反约束),所以它可以做任何事情。

解释#2:可能它只是像本地自动变量一样存储在堆栈中,您确实可以更改它。

【讨论】:

  • 我使用了一个指向const 限定变量的指针来多次更改值。难道我们不应该只允许初始化一次吗?等一下。我正在发布一个编辑。检查编辑
  • @Rüppell'sVulture int *p = &amp;foo; p 不是 const-qualified。我也没有看到多次更改它的相关性。赋值不是初始化,是一个或多个赋值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-02
  • 1970-01-01
  • 2018-08-15
相关资源
最近更新 更多