在C++中将多维数组转换为指针

20

我有一个看起来像下面这样的程序:

double[4][4] startMatrix;
double[4][4] inverseMatrix;
initialize(startMatrix) //this puts the information I want in startMatrix

我现在想计算startMatrix的逆矩阵并将其放入inverseMatrix中。 我有一个用于此目的的库函数,其原型如下:

void MatrixInversion(double** A, int order, double** B)

我需要将 double[4][4] 转换为 double** 以传递给函数,该函数对 A 进行求逆并将结果放入 B 中。问题在于我不知道如何进行转换。我尝试了"显而易见的方式":

MatrixInversion((double**)startMatrix, 4, (double**)inverseMatrix))

但这似乎不起作用。那真的是正确的做法吗?


&startMatrix, 4, &inverseMatrix - James Black
1
为什么不制作一个矩阵类,而不是按照C语言的方式进行操作?(将对象传递给函数,而不是在对象上调用方法) - GManNickG
3
@GMan:由于原帖中提到该函数是“库函数”,因此很可能没有改变接口的自由。无论是否使用类,他最终都必须获取正确的“double **”以传递给该函数。 - AnT stands with Russia
可能是将2D数组转换为指向指针的指针的重复问题。 - kiranpradeep
8个回答

29
不,没有特定的正确方法。 double[4][4] 数组无法转换为 double ** 指针。这是实现2D数组的两种不兼容的选择。需要更改某些内容:要么更改函数的接口,要么更改作为参数传递的数组的结构。
最简单的方法是后者,即使您现有的 double[4][4] 数组与函数兼容,是创建临时的“索引”数组,类型为 double *[4],指向每个矩阵中每行的开头。
double *startRows[4] = { startMatrix[0], startMatrix[1], startMatrix[2] , startMatrix[3] };
double *inverseRows[4] = { /* same thing here */ };

并传递这些“index”数组。

MatrixInversion(startRows, 4, inverseRows);

当函数完成工作后,您可以忘记 startRows inverseRows 数组,因为结果会被正确地放置到您原始的 inverseMatrix 数组中。


7

由于二维数组(一个连续的内存块)和指针数组(不连续)非常不同,因此您不能将二维数组传递给使用指向指针的函数。

您可以尝试使用模板。将第二个维度的大小作为模板参数。

#include <iostream>

template <unsigned N>
void print(double a[][N], unsigned order)
{
    for (unsigned y = 0; y < order; ++y) {
        for (unsigned x = 0; x < N; ++x) {
            std::cout << a[y][x] << ' ';
        }
        std::cout << '\n';
    }
}

int main()
{
    double arr[3][3] = {{1, 2.3, 4}, {2.5, 5, -1.0}, {0, 1.1, 0}};
    print(arr, 3);
}

另一种稍微笨拙的方法可能是使函数接受指向一维数组的指针,并将宽度和高度作为参数给出,并自己计算索引以转换成二维表示。

#include <iostream>

void print(double *a, unsigned height, unsigned width)
{
    for (unsigned y = 0; y < height; ++y) {
        for (unsigned x = 0; x < width; ++x) {
            std::cout << a[y * width + x] << ' ';
        }
        std::cout << '\n';
    }
}

int main()
{
    double arr[3][3] = {{1, 2.3, 4}, {2.5, 5, -1.0}, {0, 1.1, 0}};
    print(&arr[0][0], 3, 3);
}

当然,矩阵是值得拥有自己的类的东西(如果您需要编写辅助函数,则上述内容仍然可能相关)。

2

由于您正在使用C++,处理这类问题的正确方式是使用自定义类和一些模板。以下示例比较粗糙,但它能够基本说明问题。

#include <iostream>

using namespace std;

template <int matrix_size>
class SquareMatrix
{
    public:
        int size(void) { return matrix_size; }
        double array[matrix_size][matrix_size];
        void copyInverse(const SquareMatrix<matrix_size> & src);
        void print(void);
};

template <int matrix_size>
void SquareMatrix<matrix_size>::copyInverse(const SquareMatrix<matrix_size> & src)
{
    int inv_x;
    int inv_y;

    for (int x = 0; x < matrix_size; x++)
    {
        inv_x = matrix_size - 1 - x;
        for (int y = 0; y < matrix_size; y++)
        {
            inv_y = matrix_size - 1 - y;
            array[x][y] = src.array[inv_x][inv_y];
        }
    }
}

template <int matrix_size>
void SquareMatrix<matrix_size>::print(void)
{
    for (int y = 0; y < 4; y++)
    {
        for (int x = 0; x < 4; x++)
        {
            cout << array[x][y] << " ";
        }   
        cout << endl;
    }
}

template <int matrix_size>
void Initialize(SquareMatrix<matrix_size> & matrix);

int main(int argc, char * argList[])
{
    SquareMatrix<4> startMatrix;
    SquareMatrix<4> inverseMatrix;

    Initialize(startMatrix);

    inverseMatrix.copyInverse(startMatrix);

    cout << "Start:" << endl;
    startMatrix.print();

    cout << "Inverse:" << endl;
    inverseMatrix.print();

    return 0;
}

template <int matrix_size>
void Initialize(SquareMatrix<matrix_size> & matrix)
{
    for (int x = 0; x < matrix_size; x++)
    {
        for (int y = 0; y < matrix_size; y++)
        {
            matrix.array[x][y] = (x+1)*10+(y+1);
        }
    }
}

我喜欢你的想法,这是正确的C++方式。无论是否可行,这都是一个很好的例子。 - Test
还可以接受一个模板类型T,这就是元素将会是什么。而且可能要将大小设为无符号,因为负数大小没有意义。 - GManNickG
1
然而,将一个相对较重的操作(顺便说一下,它是逆操作,而不是转置)实现为一个模板函数参数化矩阵大小可能会导致代码膨胀,因为代码将针对每个特定大小重新实例化。处理这个问题的正确技术是通过一个以运行时大小为参数的函数来实现大部分功能(就像原始问题中那样),然后在该函数之上构建一个“薄”模板。但这又把我们带回了最初的问题。 - AnT stands with Russia

1

二维数组不是指向指针或类似内容的指针。您的 startMatrix 的正确类型是 double (*)[4]。对于您的函数,签名应该如下:

MatrixInversion( double (*A)[4], int order, double (*B)[4] );

2
该函数显然适用于任何大小(阶数)的方阵。仅限于4x4矩阵是难以接受的。此外,现在传递“order”没有意义。 - AnT stands with Russia
@AndreyT,我只是在向他/她展示如何做。如果我想要它变得通用,我可以向他展示如何拥有一个代表矩阵的类。 - leiz
在一个方阵中,行数与列数相同。您已经将列数硬编码为4。现在没有必要再传递行数了 - 它也必须是4,只能是4。 - AnT stands with Russia
OP 调用了函数 "库函数"。这通常意味着没有自由更改函数的接口。 - AnT stands with Russia
@AndreyT,好的,我没有看到那是一个库函数。 - leiz

0

有一种解决方案是使用指针通过bobobobo进行指向。

William Sherif(bobobobo)使用了C版本,我只想展示bobobobo答案的C++版本。

int numRows = 16 ;
int numCols = 5 ;
int **a ;

a = new int*[ numRows* sizeof(int*) ];
for( int row = 0 ; row < numRows ; row++ )
{
    a[row] = new int[ numCols*sizeof(int) ];
}

其余的代码与bobobobo的相同。


0

如果你想的话,你绝对可以做像下面这样的代码。

template <typename T, int n>
class MatrixP
{
public:
    MatrixP operator()(T array[][n])
    {
        for (auto i = 0; i < n; ++i) {
            v_[i] = &array[i][0];
        }

        return *this;
    }

    operator T**()
    {
        return v_;
    }

private:
    T* v_[n] = {};
};

void foo(int** pp, int m, int n)
{
    for (auto i = 0; i < m; ++i) {
        for (auto j = 0; j < n; ++j) {
            std::cout << pp[i][j] << std::endl;
        }
    }
}

int main(int argc, char** argv)
{
    int array[2][2] = { { 1, 2 }, { 3, 4 } };
    auto pa = MatrixP<int, 2>()(array);

    foo(pa, 2, 2);
}

-1
问题在于,二维数组与指针数组不同。 二维数组将元素按行存储 - 因此,当您传递这样的数组时,只会给出指向开头的指针。 接收函数可以计算如何查找数组的任何元素,但是仅当它知道每行的长度时才能这样做。
因此,您的接收函数应声明为void MatrixInversion(double A [4] [],int order,double B [4] [])

1
如果您知道数组的大小并且知道它是如何填充的,您可以将其作为&A传递,但是您需要事先了解很多内容,但这是可能的。 - James Black
这不是正确的声明。在数组参数声明中,只有第一个大小可以省略。而且,固定矩阵的大小会破坏函数的预期灵活性(即其处理任何大小的矩阵的能力)。 - AnT stands with Russia
@James Black:没错。如果我们无论如何都要修复正方形矩阵的大小,声明参数的正确方式应该是 double (&A)[4][4],而不是 double A[][4]。至少它会保留完整的数组类型,而不是将其降级为 double (*)[4] - AnT stands with Russia

-2

如果使用C++,请写出漂亮的代码:

struct matrix {
    double m[4][4];
};

matrix startMatrix;
matrix inverseMatrix;

因此,接口将会是:

void MatrixInversion(matrix &A, int order, matrix &B);

使用它。
MatrixInversion(startMatrix, 4, inverseMatrix);

好处

  1. 界面非常简单清晰。
  2. 一旦需要在矩阵内部修改“m”,您无需更新界面。

或者这样

struct matrix {
    void Inversion(matrix &inv, int order) {...}
protected:
    double m[4][4];
};

matrix startMatrix;
matrix inverseMatrix;
...

一个丑陋的C语言方法

void MatrixInversion(void *A, int order, void *B);
MatrixInversion((void*)startMatrix, 4, (void*)inverseMatrix);

编辑:矩阵求逆的参考代码,不会崩溃:

void MatrixInversion(void *A, int order, void *B)
{
    double _a[4][4];
    double _b[4][4];

    memcpy(_a, A, sizeof _a);
    memcpy(_b, B, sizeof _b);
    // processing data here

    // copy back after done
    memcpy(B, _b, sizeof _b);
}

你的“丑陋的C语言方式”只会导致程序崩溃。如果你注意到了,原帖的作者已经尝试过了,但由于明显的原因它并没有起作用。 - AnT stands with Russia
实际上,它不会崩溃,因为你已经知道一个二维双精度数组不仅仅是double**,所以你可以安全地处理它。请参考我的代码。 - Test
@AndreyT,你让我失望了,你什么都不懂。如果有问题,请你自己写一些代码来进行测试,而不是只说A不好,B也不行。 - Test
你还没有解释如何处理一个5x5的矩阵,以及为什么在编译时顺序已经固定了,却还要传递“order”。 - AnT stands with Russia
@AndreyT,嗯,你很有趣。OP说:“问题是我需要知道如何将double [4] [4]转换为double **以提供给函数”,问题等同于:“为什么不能在MatrixInversion接口上使用double **对应于double startMatrix [4] [4]数组?”几乎每个人都给出了正确的解释,包括你。那么你为什么要和每个人争论呢?展示你是一位理想主义者吗? - Test
显示剩余5条评论

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