c-用malloc创建一个三维数组,但得到RE和MLE



我编写了一个函数,其中包含三个参数,即高度、宽度和深度。在用malloc创建了一个三维数组之后,我返回一个unsigned***作为数组的名称。

另一个功能是删除这个动态三维数组。使用数组的名称作为参数,并在函数中释放它。

但我在提交中得到了一些RE和MLE,我想知道我该怎么办?

unsigned ***new_3d_array(unsigned n, unsigned m, unsigned k)
{
unsigned ***arr3D = (unsigned***)malloc(n * sizeof(unsigned**));
int i, j;
for (i = 0; i < n; i++)
{
arr3D[i] =  (unsigned**)malloc(m * sizeof(unsigned*));
for (j = 0; j < m; j++)
{
arr3D[i][j] = (unsigned*)malloc(k * sizeof(unsigned));
}
}
return arr3D;
}
void delete_3d_array(unsigned ***arr)
{
int i = 0, j = 0;
while (arr[i] != NULL)
{
j = 0;
while (arr[i][j] != NULL)
{
free(arr[i][j]);
j++;
}
free(arr[i]);
i++;
}
free(arr);
}

delete_3d_array函数中,您依赖于arr[i]arr[i][j]的第一个"越界"指针元素是NULL,但这是不安全的,除非您new_3d_array函数中显式添加这些"额外"值(哨兵(,并将NULL分配给这些值:

unsigned ***new_3d_array(unsigned n, unsigned m, unsigned k)
{
unsigned ***arr3D = malloc((n+1) * sizeof(unsigned**)); /// Add space for a NULL marker.
unsigned i, j; /// As suggested by chux - Makes more sense to avoid mixing types.
arr3D[n] = NULL; /// Explicitly set the 'end marker' to NULL
for (i = 0; i < n; i++)
{
arr3D[i] = malloc((m+1) * sizeof(unsigned*)); /// Again, add end-marker...
arr3D[i][m] = NULL; // ... and definitively set it to NULL!
for (j = 0; j < m; j++)
{
arr3D[i][j] = malloc(k * sizeof(unsigned));
}
}
return arr3D;
}

另外,请参阅此处关于强制转换malloc的返回值的内容。

编辑/附录:
假设RE为"运行时错误"(即,取消引用无效/未初始化的指针(,上述代码将解决RE问题。然而,仍然存在一个由多次调用malloc(或calloc(引起的问题,称为"堆碎片"。最内部循环的每个malloc调用都会要求操作系统"寻找"空间来分配给请求的内存,这可能会(尤其是在m维度很大的情况下(让O/s"认为"内存用完了。

您可以通过较少地调用malloc来帮助防止这种情况的发生(对于较大的块(,然后计算(而不是分配(您的二级指针:

unsigned*** new_3d_array(unsigned n, unsigned m, unsigned k)
{
unsigned*** arr3D = malloc((n + 1) * sizeof(unsigned**)); // Add space for a NULL marker.
unsigned i, j;
arr3D[n] = NULL; /// Explicitly set the 'end marker' to NULL
for (i = 0; i < n; i++) {
arr3D[i] = malloc(m * sizeof(unsigned*));
unsigned* block = malloc(sizeof(unsigned) * m * k); // Allocate one BIG block of m x k 
for (j = 0; j < m; j++)
arr3D[i][j] = block + j * m;// First element (j == 0) will be "block", others will be incremented by "m"
}
return arr3D;
}

当然,您需要相应地修改delete_3d_array函数:

void delete_3d_array(unsigned*** arr)
{
int i = 0;
while (arr[i] != NULL) {
free(arr[i][0]); // This will free the WHOLE BLOCK (m x k) allocated above!
free(arr[i]);
i++;
}
free(arr);
}

理论上,你甚至可以用类似的方式改变"外部"循环;但是,计算子数组指针地址的语法变得相当晦涩难懂。

请随时要求任何进一步的澄清和/或解释。

我认为最好使用calloc,而不是malloc。界面更简单,并减少了错误初始化的可能性。

样式说明:最好使用for循环,而不是while循环来释放数据。

unsigned ***new_3d_array(unsigned n, unsigned m, unsigned k)
{
unsigned ***arr3D = calloc(n+1, sizeof(*arr3d));
for (int i = 0; i < n; i++)
{
arr3D[i] =  calloc(m+1, sizeof(*arr3D[i]));
for (int j = 0; j < m; j++)
{
arr3D[i][j] = calloc(k, sizeof(*arr3D[i][j]));
}
}
return arr3D;
}
void delete_3d_array(unsigned ***arr)
{
for (int i=0 ; arr[i] != NULL ; i++ ) {
for (int j = 0; arr[i][j] != NULL ; j++) {
free(arr[i][j]);
}
free(arr[i]);
arr[i] = NULL ;
}
free(arr);
}

@adrian给出了一个可能的解决方案。

还有另一种方法,将数组的大小传递给delete函数。您只需要将nm传递给函数。

void delete_3d_array(unsigned ***arr, int n, int m)
{
int i,j;
for (i=0; i<n; i++)
{
for (j=0; j<m; j++)
{
free(arr[i][j]);
}
free(arr[i]);
}
free(arr);
}

最新更新