将2D数组处理为一维数组。

时间:2023-02-08 07:51:37

Let's say we have a 2D int array:

假设我们有一个2D int数组:

int a[3][4] = { { 1,3,2,4 }, { 2,1,5,3 }, { 0,8,2,3 } };

Is it legal and valid to take its address and reinterpret it as a pointer to 1D array of ints? Basically:

获取它的地址并将其重新解释为指向1D ints数组的指针是否合法和有效?基本上:

int *p = reinterpret_cast<int *>(&a);

So that I can do things like (roughly):

这样我就可以做(大致)

template<typename T, size_t X, size_t Y>
void sort2(T(&arr)[X][Y])
{
    T *p = reinterpret_cast<T *>(&arr);
    std::sort(p, p + X*Y);
}

DEMO: https://ideone.com/tlm190

演示:https://ideone.com/tlm190

To my knowledge, the standard guarantees that alignment of 2D array would be contiguous in memory, and although p + X*Y technically is out of range is never accessed so should not lead to Undefined Behaviour either.

据我所知,该标准保证二维数组的对齐在内存中是连续的,而且虽然从技术上讲p + X*Y不在范围之内,所以也不应该导致未定义的行为。

Can I absolutely treat 2D arrays as 1D arrays when needed?

如果需要,我是否可以将2D数组视为1D数组?

3 个解决方案

#1


3  

From http://www.cplusplus.com/doc/tutorial/arrays/

从http://www.cplusplus.com/doc/tutorial/arrays/

int jimmy [3][5];   // is equivalent to
int jimmy [15];     // (3 * 5 = 15)  

when creating an array ( of any dimension ) the array memory is a fixed block of memory in size = sizeof(type) * dim0 * dim1 * ....;

当创建一个数组(任何尺寸)数组内存是固定大小的内存块= sizeof(类型)* dim0 * dim1 * ....;

So to your question, yes you can recast the array safely into one dimensional array.

所以对于你的问题,是的,你可以安全地将数组重铸成一维数组。

#2


3  

Thank you all for replying and commenting, but I think the correct answer is - as it stands the code exhibits technical UB, though correctable. I have looked through some of those questions [1, 2] @xskxzr linked and it led me to this quote from the standard:

谢谢大家的回复和评论,但我认为正确的答案是——正如它所代表的代码展示技术的UB,虽然是可修正的。我查阅了其中的一些问题[1,2]@xskxzr链接,它引导我引用了《标准》中的这句话:

If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a reinterpret_­cast. [ Note: An array object and its first element are not pointer-interconvertible, even though they have the same address. — end note ]

如果pointer-interconvertible两个对象,它们有相同的地址,就可以获得一个从一个指向另一指针通过reinterpret_­。[注意:数组对象及其第一个元素不是指针可交换的,即使它们有相同的地址。——结束注意)

Then on reinterpret_cast page there is the following note with an example:

然后在reinterpret_cast页面上有一个例子:

Assuming that alignment requirements are met, a reinterpret_cast does not change the value of a pointer outside of a few limited cases dealing with pointer-interconvertible objects:

假设对齐要求得到满足,reinterpret_cast不会改变指针的值,除非有几个有限的情况涉及指针间可转换对象:

int arr[2];
int* p5 = reinterpret_cast<int*>(&arr); // value of p5 is unchanged by reinterpret_cast and
                                        // is "pointer to arr"

Even though this compiles without warning and runs, this is technically a UB because p5 is technically still a pointer to arr and not to arr[0]. So basically the use of reinterpret_cast the way I used it leads to UB. Taking the above into account, if I were to create int * directly to the 1st int (and this is ok according to the answer by @codekaizer), then this should be valid, right?:

即使这个编译没有任何警告并且运行,从技术上来说,这是一个UB,因为从技术上讲,p5仍然是指向arr的指针,而不是指向arr[0]的指针。所以基本上reinterpret_cast就是我使用它的方式导致了UB。考虑到上面的情况,如果我直接创建int *到第一个int(根据@codekaizer的答案,这是可以的),那么这个应该是有效的,对吗?

template<typename T, size_t X, size_t Y>
void sort2(T(&arr)[X][Y])
{
    T *p = &arr[0][0]; // or T *p = arr[0];
    std::sort(p, p + X * Y);
}

But it probably is UB as well since the pointer p is pointing to the first T of the first array of Ts which has Y elements. p + X*Y therefore will be pointing out of range of this 1st array of Ts, hence UB (thanks again to @xskxzr for the link and comment).

但它也可能是UB,因为指针p指向第一个有Y元素的T。因此,p + X*Y将指向第一个Ts数组的范围之外,也就是UB(再次感谢@xskxzr的链接和评论)。

If the expression P points to element x[i] of an array object x with n elements, the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) element x[i+j] if 0≤i+j≤n; otherwise, the behavior is undefined.

如果表达式P指向元素x[我]x n个元素的数组对象,表达式P + J和J + P(J值)指向(possibly-hypothetical)元素x[我+ J]如果0≤我+ J≤n;否则,行为就没有定义。

So here is my final attempt before I give up:

这是我放弃之前的最后一次尝试:

template<typename T, size_t X, size_t Y>
void sort2(T(&arr)[X][Y])
{
    T(&a)[X * Y] = reinterpret_cast<T(&)[X * Y]>(arr);
    std::sort(a, a + X * Y);
}

Here T arr[X][Y] is first converted to T a[X*Y] with, again, reinterpret_cast, which I think is now valid. The reinterpreted array a happily decays to a pointer to 1st element of array a[X*Y] (a + X * Y is also within the range) and gets converted to an iterator in std::sort.

这里T arr[X][Y]首先被转换为T a[X*Y],再一次,reinterpret_cast,我认为现在是有效的。重新解释的数组a愉快地衰变为指向数组a[X*Y]的第一个元素的指针(a + X*Y也在范围内),并在std::sort中转换为迭代器。

TL;DR version

TL;博士版本

Behaviour in the OP is Undefined because of improper use of reinterpret_cast. The correct way to convert 2D array to 1D array would be:

OP中的行为没有定义,因为不正确地使用reinterpret_cast。将2D数组转换为一维数组的正确方法是:

//-- T arr2d[X][Y]
T(&arr1d)[X*Y] = reinterpret_cast<T(&)[X*Y]>(arr2d);

An lvalue expression of type T1 can be converted to reference to another type T2. The result is an lvalue or xvalue referring to the same object as the original lvalue, but with a different type. No temporary is created, no copy is made, no constructors or conversion functions are called. The resulting reference can only be accessed safely if allowed by the type aliasing rules

可以将T1类型的lvalue表达式转换为对另一个T2类型的引用。结果是一个lvalue或xvalue指向与原始lvalue相同的对象,但类型不同。没有创建临时的,没有复制,没有调用构造函数或转换函数。只有在类型别名规则允许的情况下,才能安全地访问结果引用

Aliasing rules:

混叠规则:

Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType, the behavior is undefined unless one of the following is true:

当试图通过类型AliasedType的glvalue读取或修改类型DynamicType对象的存储值时,该行为是未定义的,除非以下其中一个是正确的:

  • AliasedType and DynamicType are similar.
  • AliasedType和DynamicType是相似的。

Type similarity:

类型相似:

Informally, two types are similar if, ignoring top-level cv-qualification

非正式地,两种类型是相似的if,忽略了*的cv限定

  • they are both arrays of the same size or both arrays of unknown bound, and the array element types are similar.
  • 它们都是相同大小的数组或两个未知绑定数组,数组元素类型相似。

Array element type:

数组元素类型:

In a declaration T D where D has the form

在声明td中,D有表格

D1 [ constant-expression opt ] attribute-specifier-seq opt

D1 [constant-expression opt]属性-specifier-seq opt

and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is an array type; if the type of the identifier of D contains the auto type-specifier, the program is ill-formed. T is called the array element type;

声明T D1中的标识符类型为“derived-declarator-type-list T”,则D的标识符类型为数组类型;如果D的标识符的类型包含自动类型说明符,则程序是病态的。T称为数组元素类型;

#3


2  

Yes. It is legal and valid.

是的。这是合法和有效的。

As per dcl.array:

根据dcl.array:

If E is an n-dimensional array of rank i×j×⋯×k , then E appearing in an expression that is subject to the array-to-pointer conversion is converted to a pointer to an (n−1)-dimensional array with rank j×⋯×k. If the * operator, either explicitly or implicitly as a result of subscripting, is applied to this pointer, the result is the pointed-to (n−1)-dimensional array, which itself is immediately converted into a pointer.

如果E n维数组排名我j××⋯×k,然后E表达式中出现受array-to-pointer转换转换成一个指针(n−1维数组与排名j×⋯×k。如果*操作符,显式或隐式的结果加下标,应用于这个指针,结果是指针(n−1维数组,这本身就是立即转换成一个指针。

#1


3  

From http://www.cplusplus.com/doc/tutorial/arrays/

从http://www.cplusplus.com/doc/tutorial/arrays/

int jimmy [3][5];   // is equivalent to
int jimmy [15];     // (3 * 5 = 15)  

when creating an array ( of any dimension ) the array memory is a fixed block of memory in size = sizeof(type) * dim0 * dim1 * ....;

当创建一个数组(任何尺寸)数组内存是固定大小的内存块= sizeof(类型)* dim0 * dim1 * ....;

So to your question, yes you can recast the array safely into one dimensional array.

所以对于你的问题,是的,你可以安全地将数组重铸成一维数组。

#2


3  

Thank you all for replying and commenting, but I think the correct answer is - as it stands the code exhibits technical UB, though correctable. I have looked through some of those questions [1, 2] @xskxzr linked and it led me to this quote from the standard:

谢谢大家的回复和评论,但我认为正确的答案是——正如它所代表的代码展示技术的UB,虽然是可修正的。我查阅了其中的一些问题[1,2]@xskxzr链接,它引导我引用了《标准》中的这句话:

If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a reinterpret_­cast. [ Note: An array object and its first element are not pointer-interconvertible, even though they have the same address. — end note ]

如果pointer-interconvertible两个对象,它们有相同的地址,就可以获得一个从一个指向另一指针通过reinterpret_­。[注意:数组对象及其第一个元素不是指针可交换的,即使它们有相同的地址。——结束注意)

Then on reinterpret_cast page there is the following note with an example:

然后在reinterpret_cast页面上有一个例子:

Assuming that alignment requirements are met, a reinterpret_cast does not change the value of a pointer outside of a few limited cases dealing with pointer-interconvertible objects:

假设对齐要求得到满足,reinterpret_cast不会改变指针的值,除非有几个有限的情况涉及指针间可转换对象:

int arr[2];
int* p5 = reinterpret_cast<int*>(&arr); // value of p5 is unchanged by reinterpret_cast and
                                        // is "pointer to arr"

Even though this compiles without warning and runs, this is technically a UB because p5 is technically still a pointer to arr and not to arr[0]. So basically the use of reinterpret_cast the way I used it leads to UB. Taking the above into account, if I were to create int * directly to the 1st int (and this is ok according to the answer by @codekaizer), then this should be valid, right?:

即使这个编译没有任何警告并且运行,从技术上来说,这是一个UB,因为从技术上讲,p5仍然是指向arr的指针,而不是指向arr[0]的指针。所以基本上reinterpret_cast就是我使用它的方式导致了UB。考虑到上面的情况,如果我直接创建int *到第一个int(根据@codekaizer的答案,这是可以的),那么这个应该是有效的,对吗?

template<typename T, size_t X, size_t Y>
void sort2(T(&arr)[X][Y])
{
    T *p = &arr[0][0]; // or T *p = arr[0];
    std::sort(p, p + X * Y);
}

But it probably is UB as well since the pointer p is pointing to the first T of the first array of Ts which has Y elements. p + X*Y therefore will be pointing out of range of this 1st array of Ts, hence UB (thanks again to @xskxzr for the link and comment).

但它也可能是UB,因为指针p指向第一个有Y元素的T。因此,p + X*Y将指向第一个Ts数组的范围之外,也就是UB(再次感谢@xskxzr的链接和评论)。

If the expression P points to element x[i] of an array object x with n elements, the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) element x[i+j] if 0≤i+j≤n; otherwise, the behavior is undefined.

如果表达式P指向元素x[我]x n个元素的数组对象,表达式P + J和J + P(J值)指向(possibly-hypothetical)元素x[我+ J]如果0≤我+ J≤n;否则,行为就没有定义。

So here is my final attempt before I give up:

这是我放弃之前的最后一次尝试:

template<typename T, size_t X, size_t Y>
void sort2(T(&arr)[X][Y])
{
    T(&a)[X * Y] = reinterpret_cast<T(&)[X * Y]>(arr);
    std::sort(a, a + X * Y);
}

Here T arr[X][Y] is first converted to T a[X*Y] with, again, reinterpret_cast, which I think is now valid. The reinterpreted array a happily decays to a pointer to 1st element of array a[X*Y] (a + X * Y is also within the range) and gets converted to an iterator in std::sort.

这里T arr[X][Y]首先被转换为T a[X*Y],再一次,reinterpret_cast,我认为现在是有效的。重新解释的数组a愉快地衰变为指向数组a[X*Y]的第一个元素的指针(a + X*Y也在范围内),并在std::sort中转换为迭代器。

TL;DR version

TL;博士版本

Behaviour in the OP is Undefined because of improper use of reinterpret_cast. The correct way to convert 2D array to 1D array would be:

OP中的行为没有定义,因为不正确地使用reinterpret_cast。将2D数组转换为一维数组的正确方法是:

//-- T arr2d[X][Y]
T(&arr1d)[X*Y] = reinterpret_cast<T(&)[X*Y]>(arr2d);

An lvalue expression of type T1 can be converted to reference to another type T2. The result is an lvalue or xvalue referring to the same object as the original lvalue, but with a different type. No temporary is created, no copy is made, no constructors or conversion functions are called. The resulting reference can only be accessed safely if allowed by the type aliasing rules

可以将T1类型的lvalue表达式转换为对另一个T2类型的引用。结果是一个lvalue或xvalue指向与原始lvalue相同的对象,但类型不同。没有创建临时的,没有复制,没有调用构造函数或转换函数。只有在类型别名规则允许的情况下,才能安全地访问结果引用

Aliasing rules:

混叠规则:

Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType, the behavior is undefined unless one of the following is true:

当试图通过类型AliasedType的glvalue读取或修改类型DynamicType对象的存储值时,该行为是未定义的,除非以下其中一个是正确的:

  • AliasedType and DynamicType are similar.
  • AliasedType和DynamicType是相似的。

Type similarity:

类型相似:

Informally, two types are similar if, ignoring top-level cv-qualification

非正式地,两种类型是相似的if,忽略了*的cv限定

  • they are both arrays of the same size or both arrays of unknown bound, and the array element types are similar.
  • 它们都是相同大小的数组或两个未知绑定数组,数组元素类型相似。

Array element type:

数组元素类型:

In a declaration T D where D has the form

在声明td中,D有表格

D1 [ constant-expression opt ] attribute-specifier-seq opt

D1 [constant-expression opt]属性-specifier-seq opt

and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is an array type; if the type of the identifier of D contains the auto type-specifier, the program is ill-formed. T is called the array element type;

声明T D1中的标识符类型为“derived-declarator-type-list T”,则D的标识符类型为数组类型;如果D的标识符的类型包含自动类型说明符,则程序是病态的。T称为数组元素类型;

#3


2  

Yes. It is legal and valid.

是的。这是合法和有效的。

As per dcl.array:

根据dcl.array:

If E is an n-dimensional array of rank i×j×⋯×k , then E appearing in an expression that is subject to the array-to-pointer conversion is converted to a pointer to an (n−1)-dimensional array with rank j×⋯×k. If the * operator, either explicitly or implicitly as a result of subscripting, is applied to this pointer, the result is the pointed-to (n−1)-dimensional array, which itself is immediately converted into a pointer.

如果E n维数组排名我j××⋯×k,然后E表达式中出现受array-to-pointer转换转换成一个指针(n−1维数组与排名j×⋯×k。如果*操作符,显式或隐式的结果加下标,应用于这个指针,结果是指针(n−1维数组,这本身就是立即转换成一个指针。