我一直在阅读很多关于分配内存的文章,我认为我理解这个概念,但我被告知我必须使用如下所示的方法:
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;
}
然后当然,我稍后将不得不释放内存 - 但我只是不太理解这种方法,更具体地说,我无法真正看到最后一行会发生什么,其中剩余的指针被设置到内存块中(我被告知。而且我不确定完成分配后如何在矩阵/数组中插入元素。
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 *));
此行为 m 双指针分配空间。
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[i] 从 A[0] 开始 i*n 双精度。
因为所有这些都在一个坚实的内存块中,我们可以做一些有趣的事情。例如,A[0][n] 与 A[1][0] 完全相同。
此外,因为所有内容都在一个大块内存中,要访问 A[i][j] 对于任何 i <m、j>
内存管理是一个难以理解的话题,需要一些时间。希望这更有意义,我希望我没有让你更加困惑:)
使用这种分配形式,首先将指针数组分配给其他数组,如下所示:
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] |
+---+ +-----------+-----------+ +-------------+
所以基本上你在这里所做的是分配A[0] + i*j + j
0单独的M
元素数组T
,然后你在一个N
元素数组中收集指向这些数组的指针T *
。
你可以像任何普通的2D数组一样a[i][j]
访问每个元素;请记住,表达式a[i]
被定义为*(a + i)
;我们从a
中的地址偏移i
元素(不是字节!(,然后取消引用结果。 所以a[i][j]
被评估为*(*(a + i) + j )
.
因此,使用这种分配形式要记住几件事:
数组的"行"在内存中不会是连续的;内存中的对象跟随
a[i][M-1]
(很可能(不会a[i+1][0]
。由于每个"行"
a[i]
都分配了对malloc
的调用,因此在释放a
之前,还必须使用相应的free
调用显式解除分配(始终以与您malloc
相反的顺序free
(。尽管我们可以将
a
视为 2D 数组,但它没有数组类型,因此您无法使用sizeof a
技巧确定数组的大小;您只会获得指针类型的大小,而不是数组的总大小。 因此,您需要自己跟踪数组大小。
您必须使 poitners 指针的每个指针指向有效的 malloc()
ed 数据。
for (int i = 0 ; i < n ; ++i)
A[i] = (double *) malloc(m * sizeof(double));
您也可以一次分配所有内容,但随后符号A[i][j]
将不起作用。