怎样将简单指针转换成固定大小的多维数组?

32

我有一个函数,它接受一个指向浮点数数组的指针。根据其他条件,我知道该指针实际上指向一个2x2或3x3矩阵。(事实上,最初分配的内存就是如此,例如 float M[2][2])重要的是,我想在函数体中进行这个判断,而不是作为函数参数。

void calcMatrix( int face, float * matrixReturnAsArray )
{
    // Here, I would much rather work in natural matrix notation
    if( is2x2 )
    {
        // ### cast matrixReturnAsArray to somethingAsMatrix[2][2]
        somethingAsMatrix[0][1] = 2.002;
        // etc..
    }
    else if(is3x3)
    { //etc...
    }

}

我知道我可以使用模板和其他技术来更好地解决这个问题。我的问题实际上是关于如何在###注释处进行这样的转换。使用C++工作。


2
我认为你正在尝试解决错误的问题(即“如何转换?”很少是正确的问题)。我曾经为“如何轻松使用多维数组?”这个问题编写了一个简单的解决方案:http://ideone.com/gytw7 - R. Martinho Fernandes
除非进行了不应该进行的可怕转换(如果编译器允许也会让我惊讶),否则 float * 没有指向多维任何内容的方法。 float * 指向一个浮点数,它可能是单维浮点数组中的第一个值。但它不指向任何子数组,正如您需要多维数组一样。2x2 和 3x3 都是二维的,因此两者都可以是 float **。不过,实际上,您最好创建(或查找)并使用专用的 Matrix 类。 - KRyan
2
@DragoonWraith:很抱歉,但你是错的。float a[2][2]float**不兼容。 - Ben Voigt
4
@DragoonWraith说的仍然不正确。float a[2][2]; 仍然是一个存储在连续内存中的单个 float 序列,但具备编译器提供的二维地址计算。你可以写 float* p = &a[0][0]; 然后自己进行索引计算。 - Ben Voigt
显示剩余3条评论
4个回答

47
float (*somethingAsMatrix)[2] = (float (*)[2]) matrixReturnAsArray;

谢谢。现在肯定可以工作了。我只是想澄清一下,关于声明情况下float aMDarray [3] [3]的元素保证连续排列的观念是否存在误解。 - NoahR
2
@NoahR:这是连续的,并且与您建议的用法兼容。 - Ben Voigt
我尝试了这种技术,但Mac OS X C++编译器显示“error: assigning to 'double ()[LDN]' from incompatible type 'double ()[LDN]'”。请注意,两个报告的类型是相同的!那么为什么它们不能被分配?我的代码看起来像这样:double (*F)[LDN]; F=(double (*)[LDN])AF; 我还尝试在声明中初始化,但得到了类似的错误消息。 - Jason
1
为什么这个能够工作?是否有关于这种转换和指针与数组符号混合的类型的清晰文档?谢谢。 - Hugo
@Hugo 是的,没错 - cdecl.org 上写着 "将 identifier 声明为指向包含 2 个 float 元素的数组的指针"。 - ecatmur
显示剩余4条评论

8

float * 可以指向一个数组的第一个元素,并且应该可以重新解释为该数组类型。该转换的结果可以指向一个 float [][] 的第一个元素,因此也应该可以重新解释为该类型,以此类推。您应该能够组合这些转换并直接执行:

float (&arr)[2][2] = *reinterpret_cast<float (*)[2][2]>(matrixReturnAsArray);

类型为float **的参数不同,不能以这种方式使用。

为了避免未定义的行为,指针必须来源于实际的多维数组,如果直接使用float*,则只能访问多维矩阵的第一行。

void foo(float *f) {
    f[3] = 10.;

    float (&arr)[2][2] = *reinterpret_cast<float (*)[2][2]>(f);
    arr[1][1] = 10.;
}

void main() {
    float a[2][2];
    foo(&a[0][0]); // f[3] = 10.; is undefined behavior, arr[1][1] = 10. is well defined

    float b[4];
    foo(&b[0]); // f[3] = 10.; is well-defined behavior, arr[1][1] = 10. is undefined
}

给定float arr[2][2];,无法保证&arr[0][1] + 1&arr[1][0]相同,据我所知。因此,虽然您可以通过执行f[i*width+j]来将单个一维数组用作多维数组,但不能将多维数组视为单个一维数组。

最好使用C++的编译时类型安全,而不仅仅依靠不意外传递错误或执行错误的reinterpret_cast。要使用原始数组获得类型安全性,应该使用引用到您想要的原始数组类型:

void foo(float (&f)[2][2]) {}
void foo(float (&f)[3][3]) {}

如果您想按值传递数组,则无法使用原始数组,而应该使用类似std::array的东西:

void foo(std::array<std::array<float,2>,2> f) {}
void foo(std::array<std::array<float,3>,3> f) {}

1
“sizeof(T[N])”不是保证恰好为“N * sizeof(T)”吗? - Ben Voigt
1
我知道标准中有一条非规范性注释提到了那个问题。如果你能找到任何规范性的内容,请告诉我。 - bames53
1
5.3.3p2 直接说明是必需的。在我的看法中,它具有规范性(不在“注释”内)。 - Ben Voigt
1
如果没有其他的,那行代码禁止在数组末尾填充,因为那行代码是规范性的。 - Ben Voigt
如果没有其他实际暗示该行所暗示的内容,那么我不会将其视为规范性声明,我只会将其视为事实上的错误陈述和标准中的错误。 - bames53
显示剩余2条评论

1
这种类型的转换总是更加清晰,使用typedef可以更容易地处理它。
typedef float Matrix_t[2][2];

Matrix_t* someThingAsMatrix = (Matrix_t*) matrixReturnAsArray;

如果这是 C++ 而不是 C,你应该创建一个矩阵类。(或者更好的方法是寻找一个开源的矩阵类库。)

1
使用 someThingAsMatrix[1][1] = 2.0f; 会导致“float”无法分配给“float [2]”不兼容类型。 - NoahR
3
typedef仍然有用,但您需要:typedef float MatrixRow [2]; MatrixRow* someThingAsMatrix = (MatrixRow*) matrixReturnAsArray; 这相当于ecatmur的答案。 - Ben Voigt

0
如果我没记错的话:
typedef float Matrix_t[2][2];
Matrix_t &matrix = *(Matrix_t *)matrixReturnAsArray;
或者
float (&matrix2)[2][2] = *(float ( *)[2][2])matrixReturnAsArray;

在C语言中只有指针这一种方式
Matrix_t *someThingAsMatrix = (Matrix_t *)matrixReturnAsArray;
然后通过以下方式进行访问:
(*someThingAsMatrix)[1][0] = ...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接