34

我问这个是因为我的程序有两个乘法矩阵的函数,它们只乘以 4x4 和 4x1 矩阵。标题是:

 double** mult4x1(double **m1, double **m2);
 double** mult4x4(double **m1, double **m2);

他们做 m1*m2 并以 **double 的形式返回它,下面是 4x4 乘法的片段。

 double** mult4x4(double **m1, double **m2){
      double** result = (double**) malloc(sizeof(double)*4);
      for (int i = 0; i < 4; i++) {
           result[i] = (double*) malloc(sizeof(double)*4);
      }
      ...multiply...
      return result;
 }

mult4x1 和 mult4x4 之间的区别仅在于它们内部使用的索引。

我有这三个矩阵:

double m1[4][4] = {
    {2, 3, 5, 6},
    {9, 8, 1, 7},
    {5, 4, 3, 1},
    {7, 6, 1, 2}
};

double m2[4][4] = {
    {1, 0, 0, 0},
    {0, 1, 0, 0},
    {0, 0, 1, 0},
    {0, 0, 0, 1}
};

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

试图将这些矩阵相乘会发生错误。

double** test = mult4x4(m1, m2);
double** test2 = mult4x1(identity4x4(), m3);
//identity4x4() creates a 4x4 identity matrix - double** identity4x4();

产量:

错误:不能为参数double * mult4x4(double* , double *)'转换double (*)[4]' todouble* '1' to

错误:无法为参数double * mult4x1(double* , double *)'转换double (*)[1]' todouble* '2' to

double[][] 不应该等于 **double 吗?一个双精度数组。欢迎任何澄清、误解和错误。

4

9 回答 9

51

不。
Adouble**是指向双精度 ( double*) 的指针。

所以实际上它应该像这样创建(注意第一个 malloc sizeof() 中的额外 *):

  double** result = (double**) malloc(sizeof(double*)*4);
  for (int i = 0; i < 4; i++) {
       result[i] = (double*) malloc(sizeof(double)*4);
  }

所以在内存中它看起来像这样:

[] -> { d,d,d,d }
[] -> { d,d,d,d }
[] -> { d,d,d,d }
[] -> { d,d,d,d }

有 4 个缓冲区可容纳 4 个双打,但不是连续的。

虽然您的 double[4][4] 是内存中的连续缓冲区,但如下所示:

 { { d,d,d,d } { d,d,d,d } {d,d,d,d} {d,d,d,d} }
于 2013-10-28T14:09:59.660 回答
24

虽然double[N][M]and都double**让你处理二维数组,但两者绝对不是等价的:前者表示doubles 的二维数组,而后者表示指向 double 的指针,可以解释(借助方便的方括号语法C/C++ 的)作为指向 的指针数组double,或作为 的数组的数组double

double[N][M]表示“矩形”形状的 2D 数组,同时允许double**您通过为矩阵的每一行分配不同数量的内存来构建“自由形式”(锯齿状)数组。

与编译器的不同之处在于,double[N][M]{r,c}可以通过计算与数组原点的偏移量来计算元素的位置,而无需从内存中读取任何内容。但是,给定 a double**,编译器必须计算指向行的指针的地址,读取该指针,然后才计算目标元素的地址。由于这种差异,两者不可互换。

于 2013-10-28T14:07:15.617 回答
3

没有类型[][]。事实上,你所拥有的m2是一个大小为 4 的 double 类型的数组,m1它是一个大小为 1 的数组。大小为 4 的数组不等同于双指针。

于 2013-10-28T14:07:00.377 回答
2

好吧,我希望我在这里不会变得愚蠢,但是double [][]当您处理连续的内存块时也使用该符号,而double**不一定是连续的。

我认为这是错误背后的原因。即使您可以使用相同的语义来访问值,它们实际上是不同的类型。

于 2013-10-28T14:09:45.840 回答
2

[][] 不等同于**; double ** var;是一个指针的指针,正如您从内存中的分配中看到的那样,您拥有一个指针数组,其中的每个指针都指向一个值数组double

double var [4][4];被保存在内存中,它有点等价于双 *;在这种情况下,知道矩阵的大小( 4 x 4 ),所以当你使用时var[2][2],例如,它知道它在内存中的位置;var[x][y]翻译成这样:var[x + y * 4]声明可以解释为double var [16];

拉克斯万。

于 2013-10-28T14:10:46.750 回答
2

不它不是。double[n][m]为您的二维数组分配足够大的内存块。然后,您无需计算自己的索引即可获得便利。将二维数组传递给函数时,编译器要求您提供内部维度的大小,以便它可以计算出索引。

double**是指向指针的指针。由于 C 允许指针算术,它可能意味着一个指针数组,其中每个指针都指向一个双精度数组。值本身不需要驻留在连续的内存块中。正如你所看到的,它们是完全不同的野兽。

如果你有 adouble[n][m]并且想将它传递给 adouble **你将需要构建你自己的索引数组。

于 2013-10-28T14:13:13.450 回答
2

不...m1是一个有四个元素的数组,每个元素都是一个由四个元素组成的数组。与 相同m2。即使第一个“级别”从数组衰减为指针,第二个“级别”也不会。问题是为什么?让我们看一些代码:

/* double[4][4] implicitly converts to double(*)[4]
 * giving you a pointer to first element of m1 so
 * you get back an array of four doubles.
 */
double (*pm1a)[4] = m1[0];

/* This isn't right, but let's pretend that it was
 * and that what you got back was a pointer
 * to a pointer that pointed to m1 properly.
 */
double **pm1b = (double **)m1[0];

那么,如果我们做pm1a[1]和,我们假设的代码会发生什么pm1b[1]

pm1a[1]很好:它会正确前进pm1a4 * sizeof(double)因为指针指向double[4])。

pm1b[1],另一方面,它会中断:它会前进错误的数量:指向双精度的指针的大小。

但这还不是全部。还有一个更微妙的错误。如果两个维度都衰减,编译器无法知道正在访问一个数组。它将很高兴地解释pm1b[1]指向双精度的指针。然后会发生什么?它将采用存储在该位置的任何值double 并将其视为指向double.

你可以看到为什么这会是一场灾难。

于 2013-10-28T14:25:29.147 回答
1

另一个区别是,如果将其声明为 double[][](类似于 final 字段),则不能使 double 指向任何其他内存地址

但是如果你将它声明为**double,那么它可以指向任何内存位置。

于 2013-10-28T17:45:44.890 回答
1

不,它们绝对不等价,前者代表一个二维数组,而后者代表一个指向双精度指针的指针。

于 2013-10-29T10:05:19.627 回答