将C++静态二维双精度数组转换为double**

9
我程序中有这样一个矩阵:

double m[3][4] = 
    {
        {2, 4, 5, 7},
        {4, 5, 1, 12},
        {9, 12, 13, -4}
    };

我希望将其转换为double **类型。

我已经尝试了简单的double ** a =(double **)m;,但它不起作用(当我尝试读取任何值时,我会得到“访问冲突读取位置0x00000000”的错误消息,这意味着我正在尝试从NULL地址读取。)

我找到了几乎可以工作的解决方案:

double *b = &m[0][0];
double **c = &b;

当我读取字段c [0] [any]时,它可以正常工作。 但是,当我尝试从字段c [1] [0]中读取值时,出现了相同的NULL地址读取问题。

将我的double m [3] [4]数组转换为double **类型的正确方法是什么?

编辑: 您说这是不可能的。所以我会稍微改变一下问题。如何将二维双精度数组作为参数传递给函数?我的函数原型是:

void calculate(double **matrix, int n); //where matrix size is always n by n+1 

这在动态分配数组方面运作良好。我怀疑使其工作的唯一方法是分配新的动态数组并逐个复制原始静态数组中的元素...


强制类型转换不会使这个工作正常。double **需要一个指向双重指针的指针,而你没有这样的东西。你可以可能通过使用double *并显式传递数组维度来解决问题。 - Jerry Coffin
1
一个数组的数组和指向指针的指针不是同一回事。它们在内存中的布局不匹配,因此不能互换使用。 - Some programmer dude
1
使用Vaughn Cato提出的技术。如果需要一个double **,你需要提供一个double **而不是一个二维数组。 - Daniel Fischer
1
你的 calculate 函数需要一个指向数组的指针。问题在于你没有这个指针。解决方法是创建它。请阅读 Vaughn Cato 的答案。 - Drew Dormann
6个回答

14

你不能。

double** 表示指针数组,而你并没有指针数组,而是有一个双重数组。


9

你不能只是简单地转换数组。你需要创建类似于这样的东西:

double m[3][4] = 
    {
        {2, 4, 5, 7},
        {4, 5, 1, 12},
        {9, 12, 13, -4}
    };

double *marray[3] = {m[0],m[1],m[2]};
calculate(marray,3);

或者您可以使用循环:

const size_t n = 3;
double *marray[n];
for (size_t i=0; i!=n; ++i) {
    marray[i] = m[i];
}
calculate(marray,n);

在我的情况下不起作用。数组可能会有尺寸,比如100x100。而且我不会在我的代码中写 {m[0],m[1],m[2], ..., m[99]}; :) - Greg Witczak
1
@gogowitczak:在这种情况下,您可以使用循环(已更新答案)。 - Vaughn Cato
这并不是那么简单。我想array可能有不同的大小,在编译时是未知的,也就是说,n不是编译时常量。因此,marray必须动态分配。这可能没问题,但如果你想避免动态分配,可以使用模板,我在我的回答中解释了这一点。 - Cassio Neri

7

当你编写代码时

double m[3][4] 
    {
        {2, 4, 5, 7},
        {4, 5, 1, 12},
        {9, 12, 13, -4}
    };

编译器实际上会创建一个双精度数组,就好像你写成了:
double _m[] = {2, 4, 5, 7, 4, 5, 1, 12, 9, 12, 13, -4};

然而,由于C/C++类型系统的存在,编译器记住了m的类型为double [3][4],特别地,它还记住了大小为3和4。

当您编写以下代码时:

m[i][j]

编译器会将其替换为
_m[i * 4 + j];

(“4”来自于“double [3][4]”中的第二个大小。)例如, m[1][2] == 1 和 _m[1 * 4 + 2] == _m[6] == 1。
正如其他人所说,双重指针(double**)是一种不带有大小信息的不同类型。如果要将 double** a 视为一个 3 x 4 矩阵,则 a[0]、a[1] 和 a[2] 必须是指向相应行的首个元素的 double* 指针。您可以通过以下方式实现此目标:
double* rows[] = { &m[0][0], &m[1][0], &m[2][0] };
double** a = &rows[0];

一个简单的转换并不会创建上面的变量rows。让我介绍另一种可以定义rows的替代方法(但是等效的)。
double* rows[] = { &m[0][0], &m[0][0] + 4, &m[0][0] + 2 * 4};
double* rows[] = { &_m[0], &_m[4], &_m[2 * 4]};

正如你所见,只有第二个大小(即4)是必需的。通常情况下,对于多维数组,除了第一个大小外,其余所有大小都是必需的。因此,只需要一个一维数组。

double x[4] = { 1, 2, 3, 4 };

可以隐式转换为double*

double* y = x;

使用这个事实,我们还可以写成:
double* rows[] = { _m, _m + 4, _m + 2 * 4};

实际上,_m 被转化为一个指向 m[0]double*。然后,在 _m + 4 中,_m 被转化为一个指向 m[0]double*,并且向这个指针中添加了 4。因此,_m + 4 是一个指向 _m[4] 和后面三个 double 的指针。

到目前为止,我已经解释了为什么你不能将 double [3][4](或者其他任何大小)转换成 double**。现在,我将展示在你的特定情况下,如何定义 calculate 函数。

template <int N>
void calculate(double (&m)[N][N+1]) {
   // use m as a double[N][N+1]
}

你需要调用。
calculate(m);

编译器会为您推断大小为N。一般来说(即当第二维不是N + 1时),您可以编写:

template <int N, int M>
void calculate(double (&m)[N][M]) {
   // use m as a double[N][M]
}

2

如果您始终使用数组(而不是指针)进行初始化,并且能够避免在计算函数中使用指针,您可以考虑以下选项,该选项使用模板的大小推断。

template<int m, int n>
void doubleFunc(double (&mat)[m][n])
{
    for (auto i = 0; i < m; i++)
    {
        for (auto j = 0; j < n; j++)
        {
            std::cout << mat[i][j] << std::endl;
        }
    }
}

在我的快速测试中它正常工作。

double m[3][4] = 
{
    {2, 4, 5, 7},
    {4, 5, 1, 12},
    {9, 12, 13, -4}
};

doubleFunc(m);

0
你可以将一个二维数组作为函数参数传递:
void calculate(double matrix[][y], int n);

0

在 C++ 标准中支持可变长度数组之前,您的选择包括:

  • 如果您的编译器支持可变长度数组作为扩展,您可以通过函数声明传递它们,例如 void foo(std::size_t n, double a[][n+1])。请注意,编译器可能要求在使用 n 的参数声明之前传递 n 或需要一些特殊语法。
  • 您可以传递一个 double * 并在函数中手动进行索引运算:void foo(std::size_t n, double *a) { … a[row*(n+1) + column] … }
  • 您可以创建一个类来实现可变长度数组,通过其访问器函数进行索引运算。
  • 您可以为 n 个指向 double 的指针分配空间,将这些指针填充到数组的每一行,并传递该空间的地址。

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