C++中的严格别名规则和类型别名

Strict Aliasing Rule and Type Aliasing in C++

本文关键字:别名 规则 类型 C++      更新时间:2023-10-16

当违反严格的别名规则时,我试图了解未定义的行为。为了理解SO,我读了很多文章。然而,有一个问题仍然存在:我真的不明白什么时候有两种类型的非法别名。cpp参考状态:

类型别名

每当试图通过AliasedType类型的glvalue读取或修改DynamicType类型对象的存储值时,除非以下情况之一为真,否则行为是未定义的:

  • AliasedType和DynamicType相似
  • AliasedType是DynamicType的有符号或无符号变体(可能是cv限定的)
  • AliasedType是std::byte、(由于C++17)char或unsigned char:这允许将任何对象的对象表示检查为字节数组

我还在SO上找到了一个很好的例子,我清楚地看到了这个问题:

int foo( float *f, int *i ) { 
*i = 1;               
*f = 0.f;            
return *i;
}
int main() {
int x = 0;
std::cout << x << "n";   // Expect 0
x = foo(reinterpret_cast<float*>(&x), &x);
std::cout << x << "n";   // Expect 0?
}

CCD_ 1和CCD_。我没有看到和理解的是以下修改:

struct A
{
int a;
};
struct B
{
int b;
};
A foo( A *a, B *b ) { 
a->a = 1;               
b->b = 0;            
return *a;
}
int main() {
A a;
a.a = 0;

std::cout << a.a << "n";   // Expect 0
a = foo(&a, reinterpret_cast<B*>(&a));
std::cout << a.a << "n";   // Expect 0?
}

AB是相似的类型,一切都很好,还是它们是非法别名,我有未定义的行为。如果它是合法的,这是因为AB是聚合的吗(如果是,我必须更改什么才能使其成为未定义的行为)?

任何提醒和帮助都将不胜感激。

编辑关于重复的问题

我知道这篇帖子,但我看不出它们在哪里澄清了哪些类型是相似的。至少不会延伸到我能理解的程度。因此,如果你不结束这个问题,那就太好了。

不,这是不合法的,您有未定义的行为:

8.2.1价值类别〔basic.lval〕

11如果程序试图访问对象的存储值通过以下类型之一以外的glvalue行为未定义:63

(11.1)——对象的动态类型,

(11.2)-对象的动态类型的cv合格版本

(11.3)——类似于对象,

(11.4)-一个类型,它是与对象的动态类型,

(11.5)-一个类型,它是与对象的动态类型的cv合格版本,

(11.6)-一种聚合或并集类型,包括其元素或非静态数据成员中的上述类型(递归地,包括亚聚合或包含并集)、

(11.7)-一种类型,它是对象的动态类型,

(11.8)--字符、无符号字符或std::byte类型的


63)本列表旨在规定以下情况对象可以被别名化,也可以不被别名化。

在表达式b->b = a;中,未定义的行为不是由于赋值,而是由于类成员访问表达式b->b。如果这个表达式不是UB,你的代码就不会是UB。

在[expr.ref]/1中,指定类成员访问构成对对象b的访问(在->的左侧):

后面跟一个点的后缀表达式。或者箭头->,可选地,后面跟着关键字模板([temp.names]),然后跟着id表达式,这是一个后缀表达式计算点或箭头之前的后缀表达式;[67]该求值的结果与id表达式一起确定整个后缀表达式的结果。

[67]如果对类成员访问表达式进行了求值,则即使结果对于确定整个后缀表达式的值是不必要的,也会进行子表达式求值,例如,如果id表达式表示静态成员

粗体矿

因此,int0使用类型为B的表达式读取对象a的值,您引用的规则适用于此处。

关于类似的类型,reinterpret_cast部分有一些有用的解释和示例:

非正式地,两种类型是相似的if,忽略顶级cv资格:

  • 它们是相同的类型;或
  • 它们都是指针,并且被指向的类型相似;或
  • 它们都是指向同一类成员的指针,并且被指向的成员的类型相似;或
  • 它们都是大小相同的数组或边界未知的数组,并且数组元素类型相似

例如:

  • CCD_ 14和CCD_
  • CCD_ 16和CCD_
  • CCD_ 18和CCD_
  • CCD_ 20和CCD_
  • CCD_ 22和CCD_
  • CCD_ 24和CCD_
  • CCD_ 26和CCD_
  • CCD_ 28和CCD_

此规则启用基于类型的别名分析,在该分析中,编译器假设通过一种类型的glvalue读取的值不会被写入不同类型的glvalue所修改(除上述例外情况外)。

注意,作为一种非标准语言扩展,许多C++编译器放宽了这一规则,允许通过联合的非活动成员进行错误的类型访问(这种访问在C 中没有定义