将1-d阵列重新塑形为多维数组 [英] Reshaping a 1-d array to a multidimensional array

查看:174
本文介绍了将1-d阵列重新塑形为多维数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑整个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> T2 不比 T1 更严格,或者两种类型都是void。





  1. 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。将指向cv T 的指针转换为指向cv void的指针的结果指向类型为 T 驻留,就像对象是类型 T (即不是基类子对象)的最大派生对象(1.8)。


static_cast int I] [J] [K] (§5.2.9/ 13):


prvue类型指针指向cv1 void 可以转换为类型的指针cv2 T 其中 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 to T2" if an expression of type "pointer to T1" can be explicitly converted to the type "pointer to T2" 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 both T1 and T2 are standard-layout types (3.9) and the alignment requirements of T2 are no stricter than those of T1, or if either type is void.

  1. Are int[N] and int[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.

  2. Does int[I][J][K] have no stricter alignment requirements than int[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," where T is an object type, can be converted to a prvalue of type "pointer to cv void". The result of converting a "pointer to cv T" to a "pointer to cv void" points to the start of the storage location where the object of type T resides, as if the object is a most derived object (1.8) of type T (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 cv2 T," where T 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆