为什么我们允许更改"const"限定变量的值?为什么允许指针,但不允许赋值?

Rüp*_*ure 4 c pointers const constants assignment-operator

考虑以下2个程序prog1prog2.如果我尝试使用指针更改const限定变量的值,我会收到警告(而不是错误),但程序仍会运行并显示新值.但是如果我尝试使用赋值语句更改第二个程序中的值,我得到错误(不是警告).iptr"initialization discards qualifiers from pointer target type|"iassignment 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
}
Run Code Online (Sandbox Code Playgroud)

警告:初始化从指针目标类型中丢弃限定符

//prog2
#include <stdio.h>

int main()
{
const int i=8;
i=10;
printf("%d",i);
}
Run Code Online (Sandbox Code Playgroud)

错误:分配只读变量'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
}
Run Code Online (Sandbox Code Playgroud)

Dan*_*her 8

1)为什么我们允许const在任何情况下更改只读合格变量的值?它不会破坏使用const限定符的目的吗?

尝试通过赋值运算符更改const限定对象是违反约束:

6.5.16在约束条件下:

2赋值运算符应具有可修改的左值作为其左操作数.

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

修改的左值是不具有阵列型左值,不具有不完整的类型,不具有常量限定类型,并且如果它是一个结构或联合,没有任何构件(包括,递归,任何部件或具有const限定类型的所有包含聚合或联合的元素.

作为约束违规,它需要来自编译器的诊断消息,符合5.1.1.3(1):

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

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

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

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

由于它不是约束违规也不是无效语法,因此甚至不需要发出诊断消息.

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

必须,如果你试图通过用const限定型左值修改一个对象,得到的诊断信息.

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

如果您尝试通过具有非const-qualifed类型的左值修改具有const限定类型的对象,如下所示

const int i=8;
int *ptr=&i;
*ptr=9;
Run Code Online (Sandbox Code Playgroud)

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

为初始化发出了诊断消息

int *ptr = &i;
Run Code Online (Sandbox Code Playgroud)

因为这又是一个约束违规,按照6.5.16.1(1):

以下其中一项应持有:

  • 左操作数具有原子,限定或非限定算术类型,右边有算术类型;
  • 左操作数具有与右侧类型兼容的结构或联合类型的原子,限定或非限定版本;
  • 左操作数具有原子,合格,或不合格的指针类型;以及(考虑类型左操作数将具有左值变换后)两个操作数都指向兼容的类型的合格或不合格的版本,和类型指向左边有所有右边指出的那种限定词 ;
  • 左操作数具有原子,合格,或不合格的指针类型;以及(考虑类型左操作数将具有左值变换后)一个操作数是指向的对象的类型,而另一个是一个指向的一个合格的或不合格的版本void,左边指向的类型具有右边指向的所有类型的限定符;
  • 左操作数是一个原子,限定或非限定指针,右边是一个空指针常量; 要么
  • 左操作数的类型为atomic,qualified或nonqualified _Bool,右边是指针.

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

int *ptr = (int*)&i;
Run Code Online (Sandbox Code Playgroud)

而一个不能抛弃consti.

通过一个指针到已经通过铸造掉得到的非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
Run Code Online (Sandbox Code Playgroud)

2)即使由于一些奇怪的原因我们被允许更改常量值,为什么在使用指针(允许,带警告)和使用赋值更改只读const限定变量的值之间进行区分操作(根本不允许,并给我们一个错误)?

直接分配给具有const限定类型的对象不仅违反了约束,而且明显违反了所声明的语义.const明确声明一个对象说"我不希望修改该对象".

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

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