通过const左值访问非const对象是否安全?

时间:2019-03-19 08:30:43

标签: c const language-lawyer

6.5(p7)说明:

  

一个对象的存储值只能由左值访问   表达式具有以下类型之一:88)

     

-与对象的有效类型兼容的类型,

     

-与对象的有效类型兼容的类型的限定版本,

     

-是   对应于有效类型的有符号或无符号类型   对象

     

-一种类型,它是与以下类型对应的有符号或无符号类型   对象的有效类型的限定版本,

看看6.7.3(p10)

  

要使两个合格的类型兼容,两个都应具有   兼容类型的完全相同的版本;

因此doubleconst double不兼容,因为它们的类型限定符不同。

现在,我假设带符号或无符号类型是指{strong>带符号或无符号整数类型,定义为6.2.5(p4)6.2.5(p6)自{{1 }}本身未定义签名类型

现在考虑以下代码:

6.2.5

现在,我尝试申请void print_double(const double d){ printf("d = %lf\n", d); } int main(int args, const char *argv[]){ double d = 10.2; print_double(d); }

I。 6.5 (p7)

否,a type compatible the effective type of the objectconst double不兼容。

II。 double

否,a qualified version of a type compatible with the effective type of the object的合格版本将使其比const double更加合格

III / IV。

否,const double不是有符号或无符号整数类型。

此判断似乎是错误的,因为可以通过const限定的左值访问非const对象。但是我不能从double中得出结论。

2 个答案:

答案 0 :(得分:7)

  

现在考虑以下代码:

void print_double(const double d){
    printf("d = %lf\n", d);
}

int main(int args, const char *argv[]){
    double d = 10.2;
    print_double(d);
}

您的代码没有任何别名。 main中的dd中的print_double distinct 对象,因为参数总是按值传递。但是,如果我们要“修复”示例:

void print_double(const double *pd){
    printf("d = %lf\n", *pd);
}

int main(int args, const char *argv[]){
    double d = 10.2;
    print_double(&d);
}

它仍然是定义明确的。 *pd是类型const double的左值。 const doubledouble的合格版本。所有类型都与它们自己兼容(平凡)。因此第二个项目符号成立,并且该别名有效。

答案 1 :(得分:2)

如果两个指针(左值访问)可能别名与指向兼容类型的指针不同。您引用的部分是关于指针别名(又名“严格别名规则”)的,其原因如下:

  
      
  • 与对象的有效类型兼容的类型
  •   
  • 与对象的有效类型兼容的类型的合格版本
  •   

这恰好意味着一个const限定对象可能别名一个非限定对象,即使它们不是兼容类型。示例:

void f (const int* a, int* b);

a可能指向与b相同的数据。编译器不能随意假设它们指向单独的对象。

但这不适用于您的情况,因为您通过值传递参数并创建本地副本。如果您的示例是这样,则将使用别名:

double a = 10.2;

int main(int args, const char *argv[]){
    print_double(&a);
}

void print_double(const double* b){
    printf("d = %lf\n", b);
}

其中参数b是与对象的有效类型(double)兼容的类型的合格版本。因此,编译器可能不会假设b没有指向全局变量a


关于兼容性:

每当通过赋值=或通过将其作为参数传递给函数来复制对象时,都会引入简单赋值规则。6.5.16.1允许这样做:

  
      
  • 左操作数具有原子,合格或不合格的指针类型,并且(考虑到   左值转换后左操作数将具有的类型)两个操作数均为   指向兼容类型的合格或不合格版本的指针,以及指向的类型   到左边的所有限定词都指向右边;
  •   

表示int* a; const int* b = a;很好,但是const int* a; int* b = a;违反了约束。在参数传递过程中适用同样的规则,请参见6.9.1 / 10:

  

在进入函数时,每个可变修改参数的大小表达式为   评估,并且每个参数表达式的值都将转换为   相应的参数,就像通过赋值一样。

在您的情况下,将调用者中d的值复制到类型为const double的新变量中。根据简单分配的规则,这是允许的,然后您创建一个新变量-一切都很好。