通过无符号 char 别名进行对象访问,加载和存储时会发生什么?



在下面的示例中,数组不是通过其第一个元素访问的,而是通过概念上是数组的别名访问的。然而,根据 C++17/[basic.lval]/8,可以通过无符号字符访问对象的存储值。那么,认为以下断言永远不会成立是正确的吗?

void g(){
unsigned char s[]={'X'};
unsigned char (*pointer_to_array_s)[1] = &s;
unsigned char *alias_to_array_s = 
reinterpret_cast<unsigned char*>(pointer_to_array_s);
//alias_to_array_s is not a pointer whose value point to c[0] because an array 
//and its firts element are not pointer interconvertible see [basic.compound]
//*alias_to_array_s aliases s;
assert(*alias_to_array_s=='X'); //may fire?
}

事实上,alias_to_array_s不是指向s第一个元素的有效指针是由于C++17中引入的微妙之处,请参阅此问答。

现在假设我通过别名修改了数组,我可以通过直接访问数组来检索这个修改吗?

void g(){
unsigned char s[]={'X'};
unsigned char (*pointer_to_array_s)[1] = &s;
unsigned char *alias_to_array_s = 
reinterpret_cast<unsigned char*>(pointer_to_array_s);
*alias_to_array_s='Y'; //UB?
assert(s[0]=='Y');//may fire?
}

访问是可以的,但是关于该值可以断言什么?而对于 例如,我通过对象名称创建一个存储,然后通过 锯齿指针,然后通过对象名称加载,在那里 没有风险,编译器优化了最后一个负载,并通过 立即哪个等于第一家商店?

访问在 [defns.access] 中定义为:

读取或修改对象的值

因此,通过*alias_to_array_s='Y';修改值与读取它一样可以接受。

允许编译器通过 as-if 规则优化加载/存储。程序没有任何可观察的行为。如果断言通过,编译器可以自由地将g()替换为空主体,并且根本不调用它。如果您真的担心编译器对加载/存储重新排序,您应该使用volatile或查看内存障碍。

根据这些标准,数组是对象,对象可以通过unsigned char指针和引用进行检查。让我们分解一下您的代码。

首先,我们声明一个大小为 1 的unsigned char数组。

unsigned char s[]={'X'};

我们创建一个指向unsigned char[1]的指针。这与s的类型相同,所以我们很好。

unsigned char (*pointer_to_array_s)[1] = &s;

现在这是棘手的部分。您的评论暗示下一次转换将使alias_to_array_s指向s的第一个成员,这可能是 UB。然而,s是一个对象,它的指针可以reinterpret_castcharunsigned charstd::byte,以检查它的表示。因此下一行被很好地定义为创建一个指向s表示的第一个字节的指针。

unsigned char *alias_to_array_s = 
reinterpret_cast<unsigned char*>(pointer_to_array_s);

通过alias_to_array_s修改s的另一个示例也应该没问题,因为您正在修改对象表示形式的第一个字节。在unsigned char[]的情况下,这是第一个元素。这里重要的一点是,您没有将指针到数组转换为指向第一个元素的指针。将指向数组的指针转换为unsigned char *以检查其表示形式。

最新更新