2015-10-18 92 views
0

我一直在阅读大量有关分配内存的文章,我认为我理解这个概念,但我被告知必须使用类似下面的方法:为2d阵列分配内存c

double ** malloc_array2d(size_t m, size_t n) 
{ 
    double **A; 
    size_t i; 

    A = malloc(m * sizeof(double *));  
    if (A == NULL) 
     return NULL; 
    A[0] = (double *) malloc(m * n * sizeof(double)); 
    if (A[0] == NULL) 
    { 
     free(A); 
     return NULL; 
    } 

    for (i = 1 ; i < m ; i++) 
     A[i] = A[0] + i *n; 
    return A; 
} 

然后当然我必须在以后释放内存 - 但我只是不太明白这种做法,更具体地说我真的不能看到最后一行会发生什么情况,其中剩余指针被设置到内存块中(我已经被告知了,而且我不知道当我完成分配时如何将元素插入到矩阵/数组中)。

+1

您可以随时谷歌.. – wrangler

+1

而且你可太consisten,你投了'的malloc()'第二次唯一不做它在所有或做它,即使它的丑陋和不必要的,但BE是一致的。 –

+0

'for'循环非常小,尝试使用小的具体值(比如说'm = n = 2')并且只是写出函数的作用,扩展循环迭代。为分配的块和指针绘制方框和箭头......如果您仍然无法看到发生了什么,请解释您所了解的位。 – Useless

回答

1
double ** malloc_array2d(size_t m, size_t n){ 

    double **A; 
    size_t i; 

    A = malloc(m*sizeof(double *));  
    if (A == NULL) return NULL; 
    A[0]=(double *)malloc(m*n*sizeof(double)); 
    if (A[0] == NULL) {free(A); return NULL;} 
    for(i=1; i<m; i++) A[i]=A[0]+i*n; 

    return A; 
} 

让我们一行一行:

A = malloc(m*sizeof(double *)); 

此行米双指针分配空间。

A[0] = (double *) malloc(m*n*sizeof(double)); 

A [0]现在是记忆m * n个双打一个块,这是我们需要用于2D阵列的所有双打。

for (int i = 1; i < m; i++) {A[i] = A[0] + i * n;} 

因为每个A [I]为n双打块,我们希望A [1]开始我* N从A加倍远[0]。

因为所有这些都在牢固的记忆中,所以我们可以做一些有趣的事情。例如,A [0] [n]与A [1] [0]完全相同。此外,因为所有内容都在一个大块内存中,所以为了访问A [i] [j],我们只需要访问A[0] + i*j + j处的双精度值。这比去往指向双* B的A [i]并且找到B [j]要快得多。

内存管理是一个难以理解的话题,需要一些时间。希望这使得更多的意义上说,我希望我没有混淆你更:)

+0

谢谢!这完全有道理:) – Linda

-2

Yo你必须使poitners指针的每个指针指向有效的malloc() ed数据。

for (int i = 0 ; i < n ; ++i) 
    A[i] = (double *) malloc(m * sizeof(double)); 

你也可以将它分配全部一次,但随后的符号A[i][j]将无法​​正常工作。

+1

问题中的代码确实有效。它一次分配它,然后指向分配的块。 – interjay

+0

是的,我认为这是我大多数时候看到的方法,当我谷歌 - 这一个是有道理的 - 但我刚刚被告知,这是“天真”的方法,并且一个更快的方法是分配一个大块m * n的内存,然后设置然后设置指针到这块内存 - 我的书说这样做:) – Linda

2

通过这种形式分配的,您可以通过分配指向其他数组的数组开始,像这样:

T **a = malloc(sizeof *a * N); // N is the number of rows 

sizeof *a相当于sizeof (T *);数组中的每个元素将成为指向T的指针。当我们完成后,我们有一些像在内存中的以下内容:现在

+---+ 
a: | | a[0] 
    +---+ 
    | | a[1] 
    +---+ 
    | | a[2] 
    +---+ 
    ... 
    +---+ 
    | | a[N-1] 
    +---+ 

,对于每一元素,我们分配另一的内存块来保存T类型的每个元素:

a[i] = malloc(sizeof *a[i] * M); // M is the number of columns 

每个a[i]的类型为T *,所以sizeof *a[i]等于sizeof (T)

后这样做了,我们有一些看起来像这样的记忆:

+---+   +---------+---------+ +-----------+ 
a: | | a[0] ---> | a[0][0] | a[0][1] |...| a[0][M-1] | 
    +---+   +---------+---------+ +-----------+ 
    | | a[1] ---> | a[1][0] | a[1][1] |...| a[1][M-1] | 
    +---+   +---------+---------+ +-----------+ 
    | | a[2] ---> | a[2][0] | a[2][1] |...| a[2][M-1] | 
    +---+   +---------+---------+ +-----------+ 
    ... 
    +---+   +-----------+-----------+ +-------------+ 
    | | a[N-1]--> | a[N-1][0] | a[N-1][1] |...| a[N-1][M-1] | 
    +---+   +-----------+-----------+ +-------------+ 

所以基本上你所做的是分配的TN单独M - 元素数组,然后你收集指针到N-T *的元素数组中的那些数组。

您可以像访问任何普通的2D数组一样访问每个元素,如a[i][j];请记住,表达式a[i]定义为*(a + i);我们从a中的地址抵消了i元素(不是字节!),然后解除引用结果。所以a[i][j]被评估为*(*(a + i) + j)

所以,几件事情与这种形式的分配的请记住:

  1. 阵列的“行”是不会在存储器中连续;内存中的对象a[i][M-1](很有可能)不会是a[i+1][0]

  2. 由于每个“行” a[i]用呼叫分配给malloc,还必须明确与之前free相应的呼叫释放取消分配a(以相反的顺序总是freemalloc)。

  3. 尽管我们可以将a当作2D数组,但它没有数组类型,所以您无法使用sizeof a技巧来确定数组的大小;你只会得到指针类型的大小,而不是数组的总大小。所以你需要自己跟踪数组的大小。