【问题标题】:Is it undefined behavior to `reinterpret_cast` a `T*` to `T(*)[N]`?`reinterpret_cast` a `T*` to `T(*)[N]` 是未定义的行为吗?
【发布时间】:2018-07-04 18:27:08
【问题描述】:

考虑以下场景:

std::array<int, 8> a;
auto p = reinterpret_cast<int(*)[8]>(a.data());
(*p)[0] = 42;

这是未定义的行为吗?我认为是。

  • a.data()返回一个int*,与int(*)[8]不一样

  • cppreference 上的 type aliasing rules 似乎表明 reinterpret_cast 无效

  • 作为程序员,我知道a.data()指向的内存位置是8int对象的数组

我是否缺少任何规则使 reinterpret_cast 有效?

【问题讨论】:

  • std::array 必须是一个连续的容器。因此,问题归结为 int * p 使得 [p; p+N) 已知是一个有效的指针范围是否可以转换为 int[N]
  • 误导性标题。做指针转换从来都不是UB。潜在的问题是通过取消引用转换结果来访问内存

标签: c++ language-lawyer reinterpret-cast strict-aliasing c++17


【解决方案1】:

一个数组对象和它的第一个元素不是指针可相互转换的*,所以reinterpret_cast的结果是一个类型为“指向8个数组的指针int”的指针,其值为“指向a[0]的指针”1。换句话说,尽管有类型,但它实际上并不指向任何数组对象。

然后代码将数组到指针的转换应用于解引用此类指针所产生的左值(作为索引表达式(*p)[0] 的一部分)2。仅当左值实际引用数组对象3 时,才指定该转换的行为。由于这种情况下的左值没有,因此行为未定义省略4


*如果问题是“为什么数组对象和它的第一个元素不能指针互转换?”,已经有人问了:Pointer interconvertibility vs having the same address

1[expr.reinterpret.cast]/7[conv.ptr]/2[expr.static.cast]/13[basic.compound]/4

2[basic.lval]/6[expr.sub][expr.add]

3[conv.array]: "结果是一个指向数组的第一个元素的指针。"

4[defns.undefined]:未定义的行为是“本文档未强加任何要求的行为”,包括“当本文档省略任何明确的行为定义时”。

【讨论】:

  • 您是说std::array&lt;int, 8&gt; 不包含任何int[8] 子对象,还是说指针不指向该子对象?无论哪种情况,我认为这意味着 a.data() + 3 也是无效的,我们知道这应该是有效的。
  • @hvd 我是说指针指向该int[8] 子对象的子对象;即,它仍然指向数组的一个元素,而不是数组本身。您无法从指向具有reinterpret_cast 的元素的指针获取指向数组的指针。
  • @hvd 仅当您满足 no-extra-reachability 要求时,这将取决于相关阵列。
  • @hvd 问题是您是否可以使用结果指针访问该子对象之外的内容。例如,如果std::array&lt;int, 8&gt; 类似于struct {int e[8]; char dummy;};,那么指向int[8] 的指针与指向整个结构的指针是指针互转换的,因此可以到达dummy,这使得launder 未定义。
  • @PasserBy int i; *reinterpret_cast&lt;float*&gt;(&amp;i) = 1; 尝试通过引用ifloat 类型的左值修改i
【解决方案2】:

是的,行为未定义。

int*a.data() 的返回类型)与 int(*)[8] 的类型不同,因此您违反了严格的别名规则。

当然(这更符合未来读者的利益),

int* p = a.data();

完全有效,随后的表达式 p + n 也是如此,其中整数类型 n 介于 0 和 8 之间。

【讨论】:

  • 我正在投射 .data(),而不是数组本身。
  • @VittorioRomeo:哎呀。更正了,但答案仍然成立。
  • 在 99.9999% 的情况下,我发现这可能有用。我建议使用gsl::span 来解决问题
  • 我认为这个答案仍然具有误导性或有错字:*“int*std::array&lt;int, N&gt;* 完全不同” - 我不是在转换为std::array&lt;int, N&gt;*,而是转换为int(*)[8]而是。
  • @VittorioRomeo:再做一次修改——我倾向于修改这个。
猜你喜欢
  • 2014-01-19
  • 1970-01-01
  • 1970-01-01
  • 2019-03-23
  • 1970-01-01
  • 2021-02-27
  • 2022-10-13
  • 2016-09-04
  • 2016-02-01
相关资源
最近更新 更多