分配二维数组的怪异方式?

时间:2016-04-22 12:43:56

标签: c arrays multidimensional-array malloc allocation

在一个项目中,有人推动了这一行:

double (*e)[n+1] = malloc((n+1) * sizeof(*e));

据说可以创建一个(n + 1)*(n + 1)双倍的二维数组。

据说,我说,因为到目前为止,我没有人要求我告诉我这是做什么的,确切地说,它起源于何处或为什么它应该起作用(据称,它确实如此,但我和#39;我尚未购买它。)

也许我错过了一些明显的东西,但如果有人能够向我解释上述内容,我会很感激。因为个人而言,如果我们使用我们真正理解的东西,我会感觉好多了。

3 个答案:

答案 0 :(得分:87)

变量e是指向n + 1类型的double元素数组的指针。

使用e上的解除引用运算符为您提供e的基本类型,即“n + 1类型的double元素数组”。

malloc调用只是采用e的基类型(如上所述)并获取其大小,将其乘以n + 1,并将该大小传递给malloc功能。基本上分配n + 1 n + 1 double#include <cstdlib> #include <iostream> #include "Agenda.h" using namespace std; /* * */ int main(int argc, char** argv) { Agenda nueva; nueva.menuAgenda(); return 0; } 元素数组。

答案 1 :(得分:56)

这是您应该动态分配2D数组的典型方法。

  • e是指向double [n+1]类型数组的数组指针。
  • 因此,
  • sizeof(*e)会提供指向类型的类型,即一个double [n+1]数组的大小。
  • 您为n+1此类数组分配空间。
  • 您将数组指针e设置为指向此数组数组中的第一个数组。
  • 这允许您使用e作为e[i][j]来访问2D数组中的各个项目。

我个人认为这种风格更容易阅读:

double (*e)[n+1] = malloc( sizeof(double[n+1][n+1]) );

答案 2 :(得分:39)

这个习惯自然地脱离了一维数组分配。让我们从分配任意类型T的一维数组开始:

T *p = malloc( sizeof *p * N );

简单,对吧? 表达式 *p的类型为T,因此sizeof *p提供与sizeof (T)相同的结果,因此我们为{{分配了足够的空间1}} - N的元素数组。对于任何类型T 都是如此。

现在,让我们用T替换T数组类型。然后我们的分配变为

R [10]

这里的语义与<1>分配方法的完全相同;所有改变的都是R (*p)[10] = malloc( sizeof *p * N); 的类型。而不是p,现在是T *。表达式R (*)[10]的类型*p类型为T,因此R [10]相当于sizeof *p,相当于sizeof (T)。因此,我们为sizeof (R [10])的{​​{1}} N元素数组分配了足够的空间。

如果我们想要,我们可以更进一步;假设10本身是一个数组类型R。替换为R,我们得到

int [5]

相同的交易 - Rint (*p)[10][5] = malloc( sizeof *p * N); 相同,我们最终会分配一大块连续的内存,以便sizeof *psizeof (int [10][5]) N 1}} 10的数组。

这就是分配方面;接入方面怎么样?

请记住,5下标操作在指针算术方面是定义int定义为[] 1 。因此,下标运算符a[i] 隐式地取消引用指针。如果*(a + i)是指向[]的指针,则可以通过使用一元p运算符明确解除引用来访问指向的值:

T

使用*下标运算符:

T x = *p;

因此,如果[]指向数组的第一个元素,则可以通过使用指针T x = p[0]; // identical to *p 上的下标来访问该数组的任何元素:

p

现在,让我们再次执行替换操作并将p替换为数组类型T arr[N]; T *p = arr; // expression arr "decays" from type T [N] to T * ... T x = p[i]; // access the i'th element of arr through pointer p

T

一个明显的区别;我们在应用下标运算符之前明确地解除引用R [10]。我们不想下标R arr[N][10]; R (*p)[10] = arr; // expression arr "decays" from type R [N][10] to R (*)[10] ... R x = (*p)[i]; ,我们想要下标p 指向(在这种情况下,数组 {{ 1}})。由于一元p的优先级低于下标p运算符,因此我们必须使用括号将arr[0]*明确分组。但请记住,[]p相同,因此我们可以用

代替
*

或只是

*p

因此,如果p[0]指向2D数组,我们可以通过R x = (p[0])[i]; 索引到该数组,如下所示:

R x = p[0][i];

将此与上述相同的结论用p代替p

R x = p[i][j]; // access the i'th element of arr through pointer p;
               // each arr[i] is a 10-element array of R

如果R指向常规数组,或者它指向通过int [5]分配的内存,则只是相同

这个成语具有以下好处:

  1. 这很简单 - 只需一行代码,而不是零碎的分配方法
    int arr[N][10][5];
    int (*p)[10][5]; // expression arr "decays" from type int [N][5][10] to int (*)[10][5]
    ...
    int x = p[i][j][k];
    
  2. 分配的数组的所有行都是*连续的*,而上面的零碎分配方法不是这种情况;
  3. 只需调用p即可轻松取消分配数组。同样,对于零碎的分配方法,情况并非如此,您必须在释放malloc之前解除分配每个T **arr = malloc( sizeof *arr * N ); if ( arr ) { for ( size_t i = 0; i < N; i++ ) { arr[i] = malloc( sizeof *arr[i] * M ); } }

有时候,零碎的分配方法更可取,例如当你的堆碎片很严重而且你不能将你的内存分配为一个连续的块时,或者你想要分配一个“锯齿状”的数组,其中每一行可以有不同的长度。但总的来说,这是更好的方法。

<小时/> 1。请记住,数组不是指针 - 而是根据需要将数组表达式转换为指针表达式。