假设我们有一个2D int数组:
int a[3][4] = { { 1,3,2,4 }, { 2,1,5,3 }, { 0,8,2,3 } };
Run Code Online (Sandbox Code Playgroud)
获取其地址并将其重新解释为指向1D数组的指针是合法有效int的吗?基本上:
int *p = reinterpret_cast<int *>(&a);
Run Code Online (Sandbox Code Playgroud)
所以我可以做(粗略地):
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);
}
Run Code Online (Sandbox Code Playgroud)
据我所知,该标准保证2D阵列的对齐在内存中是连续的,虽然p + X*Y技术上超出范围从不被访问,因此也不应导致未定义的行为.
我可以在需要时将2D阵列绝对视为一维阵列吗?
感谢大家的回复和评论,但我认为正确的答案是 - 就目前而言,代码展示了技术 UB,但可以更正。我浏览了其中的一些问题 [ 1 , 2 ] @xskxzr 链接,这让我从标准中找到了这句话:
如果两个对象是指针的相互转换的,那么它们具有相同的地址,并且因此能够从经由一个指针到另一个获得的指针一个reinterpret_播送。[? 注意:一个数组对象和它的第一个元素是指针不可转换的,即使它们有相同的地址。——?尾注?]
然后在reinterpret_cast页面上有以下注释和示例:
假设满足对齐要求,reinterpret_cast不会在处理指针可互转换对象的一些有限情况之外更改指针的值:
int arr[2];
int* p5 = reinterpret_cast<int*>(&arr); // value of p5 is unchanged by reinterpret_cast and
// is "pointer to arr"
Run Code Online (Sandbox Code Playgroud)
即使它在没有警告的情况下编译并运行,这在技术上也是一个 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);
}
Run Code Online (Sandbox Code Playgroud)
但它也可能是 UB,因为指针p指向具有元素T的第一个Ts数组中的第一个Y。p + X*Y因此将指向第一个Ts数组的范围,因此是 UB(再次感谢@xskxzr 提供链接和评论)。
如果表达式 P 指向具有 n 个元素的数组对象 x 的元素 x[i],则表达式 P + J 和 J + P(其中 J 的值为 j)指向(可能是假设的)元素 x[i+ j] 如果 0?i+j?n; 否则,行为未定义。
所以这是我放弃之前的最后一次尝试:
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);
}
Run Code Online (Sandbox Code Playgroud)
这里T arr[X][Y]首先转换为T a[X*Y]with,再次,reinterpret_cast我认为现在有效。重新解释的数组a愉快地衰减为指向数组第一个元素的指针a[X*Y](a + X * Y也在范围内)并转换为std::sort.
TL;DR 版本
OP 中的行为是未定义的,因为reinterpret_cast. 将二维数组转换为一维数组的正确方法是:
//-- T arr2d[X][Y]
T(&arr1d)[X*Y] = reinterpret_cast<T(&)[X*Y]>(arr2d);
Run Code Online (Sandbox Code Playgroud)
T1 类型的左值表达式可以转换为对另一个类型 T2 的引用。结果是一个左值或 xvalue 引用与原始左值相同的对象,但具有不同的类型。没有临时创建,没有复制,没有构造函数或转换函数被调用。只有在类型别名规则允许的情况下才能安全地访问结果引用
别名规则:
每当尝试通过 AliasedType 类型的泛左值读取或修改 DynamicType 类型的对象的存储值时,除非满足以下任一条件,否则行为未定义:
- AliasedType 和 DynamicType 类似。
非正式地,两种类型是相似的 if,忽略顶级 cv-qualification
- 它们都是相同大小的数组或都是未知边界的数组,数组元素类型相似。
在具有以下形式的声明
TD中D
D1 [ constant-expression opt ] attribute-specifier-seq opt并且声明中标识符的类型
TD1为“ derived-declarator-type-listT”,则其标识符的类型为D数组类型;如果 的标识符类型D包含自动类型说明符,则程序格式错误。T称为数组元素类型;
来自http://www.cplusplus.com/doc/tutorial/arrays/
int jimmy [3][5]; // is equivalent to
int jimmy [15]; // (3 * 5 = 15)
Run Code Online (Sandbox Code Playgroud)
创建数组(任意维度)时,数组内存是固定的内存块size = sizeof(type) * dim0 * dim1 * ....;
所以对于你的问题,是的,你可以安全地将数组重新转换为一维数组。