使用三级指针为3D数组分配内存

5

我需要使用三级指针为一个三维数组分配内存。

#include <stdio.h>
int main()
{
    int m=10,n=20,p=30;
    char ***z;
    z = (char***) malloc(sizeof(char**)*m*n*p);
    return 0;
}

这是我这样做的正确方式吗? (我认为我所做的是错误的。)

我已经很久没写 C 了,但表面看起来这是正确的。你认为哪里出了问题? - Eric J.
1
在C语言中,不要强制转换malloc的返回值,特别是如果你没有包含stdlib.h头文件,因为函数默认情况下会返回int类型的返回值,如果没有原型。 - AndersK
@AndersK 你能否解释一下? - Rog Matthews
1
请参考https://dev59.com/dHRB5IYBdhLWcg3wgHWr,不要强制转换`malloc()`的返回值。 - hmjd
5个回答

6
要完全分配一个三维动态数组,你需要执行以下操作:
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int m=10,n=20,p=30;
    char ***z;

    z = malloc(m * sizeof(char **));
    assert(z != NULL);
    for (i = 0; i < m; ++i)
    {
        z[i] = malloc(n * sizeof(char *));
        assert(z[i] != NULL);
        for (j = 0; j < n; ++j)
        {
            z[i][j] = malloc(p);
            assert(z[i][j] != NULL);
        }
    }
    return 0;
}

释放数据的任务留给读者自行完成。


严格来说,使用断言处理运行时错误并不是很合适。 - AndersK
@Anders K:没错 - 我只是想强调 malloc 可能会失败,而这应该以某种方式处理,而不会添加太多的错误处理代码,使其遮盖其他部分的代码。我会尽力找到更好的方法... - Paul R
1
使用指针为3D数组分配内存的解释非常好。 - aliceangel

4

在C语言中,调用malloc()函数返回的指针不需要进行类型转换。

如果你希望直接存储m * n * p个字符(并自己计算地址),那么你当然不应该按照char **的大小来分配内存。

你的意思是:

int m = 10, n = 20, p = 30;
char *z = malloc(m * n * p * sizeof *z);

这将分配10 * 20 * 30 = 6000字节。可以看作是形成一个高度为p的立方体,沿垂直轴的每个“切片”都是n * m字节。

由于这是手动寻址,因此不能使用例如z[k][j][i]进行索引,而必须使用z[k * n * m + j * m + i]


2

如果您不需要内存在单个连续块中分配(在我看来通常是这种情况),则可以采用以下方式:

char ***z;
z = malloc(sizeof *z * m); // allocate m elements of char **
if (z)
{
  int i;
  for (i = 0; i < m; i++)
  {
    z[i] = malloc(sizeof *z[i] * n); // for each z[i], 
    if (z[i])                        // allocate n elements char *
    {
      int j;
      for (j = 0; j < n;j++)
      {
        z[i][j] = malloc(sizeof *z[i][j] * p); // for each z[i][j], 
        if (z[i][j])                           // allocate p elements of char
        {
           // initialize each of z[i][j][k]
        }
      }
    }
  }
}

请注意,您需要按相反的顺序释放此内存:
for (i = 0; i < m; i++)
{
  for (j = 0; j < n; j++)
    free(z[i][j];
  free(z[i]);
}
free(z);

如果你确实需要内存在连续的块中分配,你有几种选择。你可以分配一个单一的块并手动计算偏移量:

char *z = malloc(sizeof *z * m * n * p); // note type of z!
...
z[i * m + j * n + k] = some_value();

完成后,您只需要执行一次free即可:
free(z);

如果您有一个C99编译器或支持可变长度数组的C11编译器,您可以像这样做:
int m=..., n=..., p=...;
char (*z)[n][p] = malloc(sizeof *z * m);

这声明了z为一个指向char类型的nxp数组的指针,我们分配m个这样的元素。内存是连续分配的,您可以使用正常的三维数组索引语法(z[i][j][k])。与上面的方法类似,您只需要一次free调用即可。
free(z);

如果您没有C99编译器或支持VLAs的C11编译器,您需要将np设置为编译时常量,例如:

#define n 20
#define p 30

否则最后一种方法无法工作。
编辑
在这种情况下,m不需要是编译时常量,只需要np

在C语言中,一个被const修饰的对象不是编译时常量,而这是C89对于数组维度所要求的。而C++在这方面有所不同。 - John Bode

0
你需要使用 sizeof(char) 而不是 sizeof(char**),因为后者会给你一个指针的大小,在大多数现代系统上将会是4个字节,而不是你期望的1个字节。

1
你甚至不需要 sizeof(char),因为根据定义,它等于1。 - Paul R

0
你需要以下的嵌套循环 -
z = (char**)malloc(sizeof(char*) * m);
for (int i = 0; i < m; ++i)
{
    *(z + i) = (char*)malloc(sizeof(char*) * n);
    for (int j = 0; j < n; ++j)
    {
        *(*(z + i)) = (char)malloc(p);
    }
}

可能不是语法上准确的,但应该大致是这样的。


在C语言中,永远不要将malloc的结果强制转换。 - Paul R
1
请使用数组索引符号而不是显式解引用:z[i] 而不是 *(z + i) - John Bode

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接