如何在C中使用动态多维数组?

rpf*_*rpf 59 c arrays dynamic

有人知道如何使用C动态分配多维数组吗?那可能吗?

sch*_*der 76

使用动态分配,使用malloc:

int** x;

x = malloc(dimension1_max * sizeof(int*));
for (int i = 0; i < dimension1_max; i++) {
  x[i] = malloc(dimension2_max * sizeof(int));
}

[...]

for (int i = 0; i < dimension1_max; i++) {
  free(x[i]);
}
free(x);
Run Code Online (Sandbox Code Playgroud)

这将分配一个大小为dimension1_max*的2D数组dimension2_max.因此,例如,如果您需要640*480阵列(图像的fe像素),请使用dimension1_max= 640,dimension2_max= 480.然后,您可以使用x[d1][d2]where d1= 0..639,d2= 0..479 访问阵列.

但是搜索SO或Google也会发现其他可能性,例如在这个SO问题中

请注意,在这种情况下,您的数组不会分配连续的内存区域(640*480字节),这可能会给出假定这一问题的函数带来问题.因此,要使数组满足条件,请使用以下命令替换上面的malloc块:

int** x;
int* temp;

x = malloc(dimension1_max * sizeof(int*));
temp = malloc(dimension1_max * dimension2_max * sizeof(int));
for (int i = 0; i < dimension1_max; i++) {
  x[i] = temp + (i * dimension2_max);
}

[...]

free(temp);
free(x);
Run Code Online (Sandbox Code Playgroud)

  • 它不是一个多维数组 - 它是指向int或数组数组的指针数组.要为真正的2D数组分配内存,您需要使用malloc(dim1*dim2*sizeof(int)).如果某个函数需要指向2D数组的指针,比如foo(int*bar [5] [6])并且你传递了x,那么就会发生奇怪的事情.请参见http://en.wikipedia.org/wiki/C_syntax#Multidimensional_arrays (12认同)
  • 这不会**分配一个2D数组,而是指向一个指针加上数组的数组.Addresing是非常不同的,它有内存开销,可能效率较低. (4认同)
  • 这不会编译,你需要将x声明为"int**",而不是"int [] []". (2认同)

Jen*_*edt 73

自13年前的C99起,C具有动态界限的2D阵列.如果你想避免在堆栈上分配这样的野兽(你应该),你可以一次轻松地分配它们,如下所示

double (*A)[n] = malloc(sizeof(double[n][n]));
Run Code Online (Sandbox Code Playgroud)

就是这样.然后,您可以轻松地使用它,就像用于2D阵列一样A[i][j].不要忘记最后一个

free(A);
Run Code Online (Sandbox Code Playgroud)

Randy Meyers撰写了一系列文章,解释了可变长度数组(VLA).

  • 很好的答案!另外:在您的示例中,两个维度的大小都相同。对于不同的大小,它看起来像这样:`double (*a)[y] = malloc(sizeof(double[x][y]));`。三个维度:`double (*a)[y][z] = malloc(sizeof(double[x][y][z]));`。等等。 (6认同)
  • 虽然这是一个漂亮而优雅的解决方案,但其美妙之处更多地体现在“A”的声明中,而不是实际的分配中;特别是,没有在任何地方分配二维数组,更不用说动态大小的数组了。malloc 只是分配一段线性的无类型内存,适合任何内容;地址“A”的初始化可以来自任何源(好吧,由于别名问题,我假设它可能来自 char 或 double 的一维数组,或者“真正的”二维数组)。运行时 `sizeof` 只是计算一个数字(当然总是正确的);-)。 (4认同)
  • 零件[2](http://www.drdobbs.com/the-new-c/184401468),[3](http://www.drdobbs.com/the-new-c/184401476?pgno=7) Meyers系列的[4](http://www.drdobbs.com/the-new-c/184401497?pgno=2)也可以从Dobbs博士那里获得. (3认同)
  • 这个问题唯一好的答案。然而,我总是更喜欢在 `sizeof()` 中使用指针变量,如下所示:`double (*A)[n] = malloc(n*sizeof(*A));` 优点是,你无法得到`sizeof()` 中的类型详细信息错误,并且它始终与 `malloc()` 参数的 `arrayLength*sizeof(*newPointerVar)` 形式相同。我相信,这种形式出现错误的机会最低。 (2认同)

dmc*_*kee 51

基本

使用[]运算符声明和访问c中的数组.以便

int ary1[5];
Run Code Online (Sandbox Code Playgroud)

声明一个包含5个整数的数组.元素从零开始编号,因此ary1[0]是第一个元素,ary1[4]也是最后一个元素.注1:没有默认初始化,因此数组占用的内存最初可能包含任何内容.注意2:ary1[5]访问未定义状态的内存(甚至可能无法访问),所以不要这样做!

多维数组实现为数组(数组(of ...)).所以

float ary2[3][5];
Run Code Online (Sandbox Code Playgroud)

声明一个由3个一维数组组成的数组,每个数组包含5个浮点数.现在ary2[0][0]是第一个数组的第一个元素,是第一个数组ary2[0][4]的最后一个元素,是最后一个数组ary2[2][4]的最后一个元素.'89标准要求这些数据是连续的(我的K&R 2nd编辑的第216页的第A8.6.2节),但似乎与填充无关.

试图在多个方面实现动态

如果在编译时不知道数组的大小,则需要动态分配数组.尝试是很诱人的

double *buf3;
buf3 = malloc(3*5*sizeof(double));
/* error checking goes here */
Run Code Online (Sandbox Code Playgroud)

如果编译器没有填充分配(在一维数组之间留出额外的空间),这应该工作.可能更安全:

double *buf4;
buf4 = malloc(sizeof(double[3][5]));
/* error checking */
Run Code Online (Sandbox Code Playgroud)

但无论哪种方式,诀窍来自解除引用时间.你不能写,buf[i][j]因为buf有错误的类型.你也不能用

double **hdl4 = (double**)buf;
hdl4[2][3] = 0; /* Wrong! */
Run Code Online (Sandbox Code Playgroud)

因为编译器希望hdl4是double的地址.你也不能使用,double incomplete_ary4[][];因为这是一个错误;

所以,你可以做什么?

  • 自己进行行和列算术
  • 在函数中分配并完成工作
  • 使用指针数组(qrdl正在讨论的机制)

自己做数学

只需计算每个元素的内存偏移量,如下所示:

  for (i=0; i<3; ++i){
     for(j=0; j<3; ++j){
        buf3[i * 5 + j] = someValue(i,j); /* Don't need to worry about 
                                             padding in this case */
     }
  }
Run Code Online (Sandbox Code Playgroud)

在函数中分配并完成工作

定义一个函数,该函数将所需大小作为参数并按正常方式继续

void dary(int x, int y){
  double ary4[x][y];
  ary4[2][3] = 5;
}
Run Code Online (Sandbox Code Playgroud)

当然,在这种情况下ary4是一个局部变量,因此不能返回它:所有与阵列的工作必须以您在函数调用的函数来完成调用.

一个指针数组

考虑一下:

double **hdl5 = malloc(3*sizeof(double*));
/* Error checking */
for (i=0; i<3; ++i){
   hdl5[i] = malloc(5*sizeof(double))
   /* Error checking */
}
Run Code Online (Sandbox Code Playgroud)

现在hdl5指向一个指针数组,每个指针指向一个双精度数组.很酷的一点是你可以使用二维数组符号来访问这个结构--- hdl5[0][2]得到第一行的中间元素---但这不过是一种不同的对象而不是二维数组声明double ary[3][5];.

这个结构比二维数组更灵活(因为行不需要长度相同),但是访问它通常会更慢并且需要更多内存(你需要一个位置来保存中间指针).

请注意,由于我没有设置任何防护,您必须自己跟踪所有阵列的大小.

算术

c不支持向量,矩阵或张量数学,你必须自己实现它,或引入一个库.

通过缩放器进行乘法以及相同等级的数组的加法和减法很容易:只需循环遍历元素并执行操作即可.内部产品同样直截了当.

外部产品意味着更多的循环.

  • @qrdl:当我读到标准的int**a1; 和int*a2 [i]; int a3 [n] [m]; 是具有不同语义的不同生物.最后一个被分配了一块连续(可能是填充的)内存而没有一组单独的中间指针...尝试printf("%d \n",sizeof(int [3] [5])/ sizeof(int)) ; (3认同)

Joh*_*ode 9

如果您在编译时知道列数,那很简单:

#define COLS ...
...
size_t rows;
// get number of rows
T (*ap)[COLS] = malloc(sizeof *ap * rows); // ap is a *pointer to an array* of T
Run Code Online (Sandbox Code Playgroud)

您可以ap像任何2D数组一样对待:

ap[i][j] = x;
Run Code Online (Sandbox Code Playgroud)

当你完成后,你将其解除分配为

free(ap);
Run Code Online (Sandbox Code Playgroud)

如果您在编译时不知道列数,但是您正在使用支持可变长度数组的C99编译器或C2011编译器,那么它仍然非常简单:

size_t rows;
size_t cols;
// get rows and cols
T (*ap)[cols] = malloc(sizeof *ap * rows);
...
ap[i][j] = x;
...
free(ap);
Run Code Online (Sandbox Code Playgroud)

如果您在编译时不知道列数并且您正在使用不支持可变长度数组的C版本,那么您将需要做一些不同的事情.如果需要在连续的块(如常规数组)中分配所有元素,则可以将内存分配为1D数组,并计算1D偏移量:

size_t rows, cols;
// get rows and columns
T *ap = malloc(sizeof *ap * rows * cols);
...
ap[i * rows + j] = x;
...
free(ap);
Run Code Online (Sandbox Code Playgroud)

如果您不需要内存是连续的,则可以遵循两步分配方法:

size_t rows, cols;
// get rows and cols
T **ap = malloc(sizeof *ap * rows);
if (ap)
{
  size_t i = 0;
  for (i = 0; i < cols; i++)
  {
    ap[i] = malloc(sizeof *ap[i] * cols);
  }
}

ap[i][j] = x;
Run Code Online (Sandbox Code Playgroud)

由于分配是一个两步过程,因此解除分配还需要分为两步:

for (i = 0; i < cols; i++)
  free(ap[i]);
free(ap);
Run Code Online (Sandbox Code Playgroud)