将1-d阵列重新塑形为多维数组 [英] Reshaping a 1-d array to a multidimensional array
问题描述
考虑整个C ++ 11标准,是否有可能任何一致的实现继承下面的第一个断言,但失败后者?
#include< cassert>
int main(int,char **)
{
const int I = 5,J = 4,K = 3;
const int N = I * J * K;
int arr1d [N] = {0};
int(& arr3d)[I] [J] [K] = reinterpret_cast assert(static_cast< void *>(arr1d)==
static_cast< void *>(arr3d)); //这是必要的吗?
arr3d [3] [2] [1] = 1;
assert(arr1d [3 *(J * K)+ 2 * K + 1] == 1); // UB?
}
如果没有,这是技术上的UB还是不,第一个断言被删除( reinterpret_cast
保证在这里保存地址)?此外,如果重塑在相反方向(3d到1d)或从6x35阵列到10x21阵列?
EDIT:如果答案是UB,因为 reinterpret_cast
,是否有一些其他严格兼容的重新塑造方式(例如,通过 static_cast
到/中间 void *
)?
reinterpret_cast
of reference
标准规定类型 T1 $如果指向
T1 c>的指针,则
可以
reinterpret_cast
/ code>可以 reinterpret_cast
指向 T2
(§5.2.10/ 11):
类型
T1
的左值表达式可以转换为类型 code> T2 如果指向T1
的类型的表达式可以显式转换为类型T2
使用reinterpret_cast。
所以我们需要确定 指向 int(*)[N]
可以转换为 int(*)[I] [J] p>
和
reinterpret_cast
指针
T1
可以 reinterpret_cast
指向 T2
$ c> T1 T2
是标准布局类型, T2
没有更严格的对齐要求 T1
(§5.2.10/ 7):
类型指向T1的指针的v被转换为类型指向cv T2的指针,结果是
static_cast< cv T2 *>(static_cast
不比(v) code>如果 T1
和T2
都是标准布局类型(3.9) code> T2T1
更严格,或者两种类型都是void。
-
是
int [N]
和int [I] [J] [K]
标准布局类型?
int
是标量类型,标量类型数组视为标准布局类型(§3.9/ 9)。
标量类型,标准布局类类型(第9条),这些类型的数组和这些类型的cv限定版本
没有比
int [N]
更严格的对齐要求。
alignof
运算符的结果给出了完整对象类型(§3.11/ 2)的对齐要求。
alignof
运算符的结果反映了在完整对象情况下类型的对齐要求。
由于这里的两个数组不是任何其他对象的子对象,它们是完整的对象。对数组应用
alignof
可以得到元素类型的对齐要求(§5.3.6/ 3):
当将
alignof
应用于数组类型时,结果应为元素类型的对齐。
< blockquote>
这两种数组类型都有相同的对齐要求。
这使得 reinterpret_cast
有效并等效于:
int(& arr3d)[I] [J] [K] = * reinterpret_cast
其中 *
和&
是内置运算符,它等效于:
int ; arr3d)[I] [J] [K] = * static_cast
static_cast
通过 void * 到
static_cast
code>是标准转换允许的(§4.10/ 2):
指向cv <$类型的prvalue c $ c> T ,其中
T
是一个对象类型,可以转换为类型为指向cv void的prvalue。将指向cvT
的指针转换为指向cv void的指针的结果指向类型为T
驻留,就像对象是类型T
(即不是基类子对象)的最大派生对象(1.8)。
static_cast
至 int I] [J] [K]
(§5.2.9/ 13):
prvue类型指针指向cv1
void
可以转换为类型的指针cv2T
其中T
是一个对象类型,cv2与cv1具有相同的cv-qualification或更高的cv-qualification。
所以演员很好!但是我们可以通过新的数组引用访问对象吗?
< h2>访问数组元素
对像 arr3d [E2]
的数组执行数组下标等效于 *((E1)+(E2))
(§5.2.1/ 1)。让我们考虑下面的数组下标:
arr3d [3] [2] [1]
首先, arr3d [3]
等效于 * (arr3d)+(3))
。左值 arr3d
经过数组到指针的转换,得到 int(*)[2] [1]
。没有要求底层数组必须是正确的类型来做这个转换。然后访问指针值(在§3.10中很好),然后将值3添加到它。这个指针运算也很好(§5.7/ 5):
如果指针操作数和结果指向同一个数组的元素对象,或者一个超过数组对象的最后一个元素,评估不会产生溢出;
这个指针被解引用给一个 int [2] [1] ]
。这对于接下来的两个下标进行相同的处理,导致在适当的数组索引处的最终 int
lvalue。由于 *
(§5.3.1/ 1)的结果,它是一个左值:
unary *运算符执行间接:应用它的表达式应该是指向对象类型的指针,或者指向函数类型的指针,结果是引用对象或函数的左值,表达式。
然后,访问实际的 int
因为左值是 int
类型(§3.10/ 10):
如果程序试图通过除以下类型之外的glvalue访问对象的存储值,则该行为未定义:
- 对象的动态类型
- [...]
所以除非我错过了一些东西。我想说这个程序是定义明确的。
Taking into consideration the entire C++11 standard, is it possible for any conforming implementation to succeed the first assertion below but fail the latter?
#include <cassert>
int main(int, char**)
{
const int I = 5, J = 4, K = 3;
const int N = I * J * K;
int arr1d[N] = {0};
int (&arr3d)[I][J][K] = reinterpret_cast<int (&)[I][J][K]>(arr1d);
assert(static_cast<void*>(arr1d) ==
static_cast<void*>(arr3d)); // is this necessary?
arr3d[3][2][1] = 1;
assert(arr1d[3 * (J * K) + 2 * K + 1] == 1); // UB?
}
If not, is this technically UB or not, and does that answer change if the first assertion is removed (is reinterpret_cast
guaranteed to preserve addresses here?)? Also, what if the reshaping is done in the opposite direction (3d to 1d) or from a 6x35 array to a 10x21 array?
EDIT: If the answer is that this is UB because of the reinterpret_cast
, is there some other strictly compliant way of reshaping (e.g., via static_cast
to/from an intermediate void *
)?
reinterpret_cast
of references
The standard states that an lvalue of type T1
can be reinterpret_cast
to a reference to T2
if a pointer to T1
can be reinterpret_cast
to a pointer to T2
(§5.2.10/11):
An lvalue expression of type
T1
can be cast to the type "reference toT2
" if an expression of type "pointer toT1
" can be explicitly converted to the type "pointer toT2
" using a reinterpret_cast.
So we need to determine if a int(*)[N]
can be converted to an int(*)[I][J][K]
.
reinterpret_cast
of pointers
A pointer to T1
can be reinterpret_cast
to a pointer to T2
if both T1
and T2
are standard-layout types and T2
has no stricter alignment requirements than T1
(§5.2.10/7):
When a prvalue v of type "pointer to T1" is converted to the type "pointer to cv T2", the result is
static_cast<cv T2*>(static_cast<cv void*>(v))
if bothT1
andT2
are standard-layout types (3.9) and the alignment requirements ofT2
are no stricter than those ofT1
, or if either type is void.
Are
int[N]
andint[I][J][K]
standard-layout types?int
is a scalar type and arrays of scalar types are considered to be standard-layout types (§3.9/9).Scalar types, standard-layout class types (Clause 9), arrays of such types and cv-qualified versions of these types (3.9.3) are collectively called standard-layout types.
Does
int[I][J][K]
have no stricter alignment requirements thanint[N]
.The result of the
alignof
operator gives the alignment requirement of a complete object type (§3.11/2).The result of the
alignof
operator reflects the alignment requirement of the type in the complete-object case.Since the two arrays here are not subobjects of any other object, they are complete objects. Applying
alignof
to an array gives the alignment requirement of the element type (§5.3.6/3):When
alignof
is applied to an array type, the result shall be the alignment of the element type.So both array types have the same alignment requirement.
That makes the reinterpret_cast
valid and equivalent to:
int (&arr3d)[I][J][K] = *reinterpret_cast<int (*)[I][J][K]>(&arr1d);
where *
and &
are the built-in operators, which is then equivalent to:
int (&arr3d)[I][J][K] = *static_cast<int (*)[I][J][K]>(static_cast<void*>(&arr1d));
static_cast
through void*
The static_cast
to void*
is allowed by the standard conversions (§4.10/2):
A prvalue of type "pointer to cv
T
," whereT
is an object type, can be converted to a prvalue of type "pointer to cv void". The result of converting a "pointer to cvT
" to a "pointer to cv void" points to the start of the storage location where the object of typeT
resides, as if the object is a most derived object (1.8) of typeT
(that is, not a base class subobject).
The static_cast
to int(*)[I][J][K]
is then allowed (§5.2.9/13):
A prvalue of type "pointer to cv1
void
" can be converted to a prvalue of type "pointer to cv2T
," whereT
is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1.
So the cast is fine! But are we okay to access objects through the new array reference?
Accessing array elements
Performing array subscripting on an array like arr3d[E2]
is equivalent to *((E1)+(E2))
(§5.2.1/1). Let's consider the following array subscripting:
arr3d[3][2][1]
Firstly, arr3d[3]
is equivalent to *((arr3d)+(3))
. The lvalue arr3d
undergoes array-to-pointer conversion to give a int(*)[2][1]
. There is no requirement that the underlying array must be of the correct type to do this conversion. The pointers value is then accessed (which is fine by §3.10) and then the value 3 is added to it. This pointer arithmetic is also fine (§5.7/5):
If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.
This this pointer is dereferenced to give an int[2][1]
. This undergoes the same process for the next two subscripts, resulting in the final int
lvalue at the appropriate array index. It is an lvalue due to the result of *
(§5.3.1/1):
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.
It is then perfectly fine to access the actual int
object through this lvalue because the lvalue is of type int
too (§3.10/10):
If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:
- the dynamic type of the object
- [...]
So unless I've missed something. I'd say this program is well-defined.
这篇关于将1-d阵列重新塑形为多维数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!