【问题标题】:Malloc a 3-Dimensional array in C?在C中Malloc一个3维数组?
【发布时间】:2011-01-19 08:31:25
【问题描述】:

我正在将一些 MATLAB 代码翻译成 C 语言,并且我正在转换的脚本大量使用具有 10*100*300 复杂条目的 3D 数组。数组的大小还取决于传感器的输入,理想情况下应该动态分配数组。到目前为止,我已经尝试了两种方法,第一种是沿线的平面一维数组

value = array[x + (y*xSize) + (z*ySize*xSize)]

这会伤害我的大脑。我也试过一个指针数组的数组

int main () {
  int ***array = malloc(3*sizeof(int**));
  int i, j;

  for (i = 0; i < 3; i++) {
    *array[i] = malloc(3*sizeof(int*));
    for (j = 0; j < 3; j++) {
      array[i][j] = malloc(3*sizeof(int));
    }
  }

  array[1][2][1] = 10;

  return 0;
}

当我尝试分配数据时会出现段错误。

在一个完美的世界中,我想使用带有数组表示法的第二种方法,以实现更简洁、更轻松的编程。有没有更好的方法在 C 中动态分配一个三维数组?

【问题讨论】:

  • 添加 #include 并从 *array[i] 中删除 *,它会在 gcc 中编译时运行
  • 我也很好奇如何实现这一点。一维数组解决方案“更干净”(这是我目前使用的那个),但是对于数字运算,由于“偏移”计算,它比静态分配的 3D 慢得多。
  • @WorldCitizeN 你真的测量过这个性能吗?当您访问静态分配的数组时,会运行相同的计算。唯一的区别是,你不写它们。
  • @lmount 通常,间接比整数乘法要昂贵得多。我希望一维数组更快。

标签: c arrays multidimensional-array


【解决方案1】:

我会选择第一个选项(单个 1D 数组),因为它会给你一个单独的内存块来播放,而不是潜在的数千个碎片化的内存块

如果访问数组的正确元素是你的头,我会编写一个实用方法来将 x、y、z 位置转换为一维数组的偏移量

int offset(int x, int y, int z) { 
    return (z * xSize * ySize) + (y * xSize) + x; 
}

【讨论】:

    【解决方案2】:

    哦,我讨厌 malloc 数组分配吗^^

    这是一个正确的版本,基本上它只是一个错误的行:

    int main () {
      int ***array = (int***)malloc(3*sizeof(int**));
      int i, j;
    
      for (i = 0; i < 3; i++) {
        // Assign to array[i], not *array[i] (that would dereference an uninitialized pointer)
        array[i] = (int**)malloc(3*sizeof(int*));
        for (j = 0; j < 3; j++) {
          array[i][j] = (int*)malloc(3*sizeof(int));
        }
      }
    
      array[1][2][1] = 10;
    
      return 0;
    }
    

    【讨论】:

    • 对,我只是习惯这样做,因为如果你不这样做,C++ 会抛出错误。
    【解决方案3】:

    C89 中无法满足您的要求,因为 C 中的数组类型只能使用编译时已知值指定。所以为了避免疯狂的动态分配,你将不得不坚持一维的方式。你可以使用一个函数来简化这个过程

    int index(int x, int y, int z) {
      return x + (y*xSize) + (z*ySize*xSize);
    }
    
    int value = array[index(a, b, c)];
    

    在 C99 中,即使维度是运行时值,您也可以使用普通数组语法:

    int (*array)[X][Y][Z] = (int(*)[X][Y][Z])malloc(sizeof *p); 
    // fill...
    int value = (*array)[a][b][c];
    

    但是,它仅适用于本地非静态数组。

    【讨论】:

      【解决方案4】:

      添加 #include "stdlib.h" 并从 *array[i] 中删除 *,它将在 Ubuntu 上的 gcc 4.4.1 中编译时运行

      如果你添加打印语句,你可以更快地找到你的错误

      #include <stdio.h>
      #include <stdlib.h>
      
      int main () {
        int ***array = malloc(3*sizeof(int**));
        int i, j;
      
        printf("%s\n","OK");
      
        for (i = 0; i < 3; i++) {
          printf("i = %i \n",i);
          array[i] = malloc(3*sizeof(int*));
          for (j = 0; j < 3; j++) {
            printf("i,j = %i,%i \n",i,j);
            array[i][j] = malloc(3*sizeof(int));
          }
        }
      
        array[1][2][1] = 10;
      
        return 0;
      }
      

      【讨论】:

      • printf() 语句可能很有用,但不是一个好习惯。 GDB 真的不难学习,并且免费为您提供打印和变量内省,所有这些都包含在一个小包中。而且您甚至不必事先决定将 printf() 放在哪里。
      【解决方案5】:

      正如其他人所说,最好分配一块连续的内存,然后自己找出索引。如果需要,您可以编写一个函数来执行此操作。但是由于您似乎有兴趣知道如何处理多个malloc() 案例,因此这里有一个示例:

      首先,我定义了一个函数free_data(),它以xlenylen 作为前两个维度大小来释放int ***。我们不需要zlen 参数,就像free() 不会占用被释放指针的长度。

      void free_data(int ***data, size_t xlen, size_t ylen)
      {
          size_t i, j;
      
          for (i=0; i < xlen; ++i) {
              if (data[i] != NULL) {
                  for (j=0; j < ylen; ++j)
                      free(data[i][j]);
                  free(data[i]);
              }
          }
          free(data);
      }
      

      函数循环遍历指针data,找到ith int ** 指针data[i]。然后,对于给定的int ** 指针,它循环遍历它,找出data[i][j] 中的jth int *,并释放它。它还需要在释放所有data[i][j] 后释放data[i],最后,它需要释放data 本身。

      现在到分配函数。该功能通过错误检查有点复杂。特别是,由于有 1 + xlen + xlen*ylen malloc 调用,我们必须能够处理任何这些调用中的失败,并释放我们迄今为止分配的所有内存。为了让事情变得更简单,我们依靠free(NULL) 是无操作的事实,所以我们在尝试分配它们之前将所有指针设置为等于NULL 的给定级别,这样如果发生错误,我们可以释放所有指针。

      除此之外,功能很简单。我们首先为xlenint ** 值分配空间,然后为每个xlen 指针分配空间给ylenint * 值,然后为每个xlen*ylen 指针分配空间zlenint 值,为我们提供xlen*ylen*zlen int 值的总空间:

      int ***alloc_data(size_t xlen, size_t ylen, size_t zlen)
      {
          int ***p;
          size_t i, j;
      
          if ((p = malloc(xlen * sizeof *p)) == NULL) {
              perror("malloc 1");
              return NULL;
          }
      
          for (i=0; i < xlen; ++i)
              p[i] = NULL;
      
          for (i=0; i < xlen; ++i)
              if ((p[i] = malloc(ylen * sizeof *p[i])) == NULL) {
                  perror("malloc 2");
                  free_data(p, xlen, ylen);
                  return NULL;
              }
      
          for (i=0; i < xlen; ++i)
              for (j=0; j < ylen; ++j)
                  p[i][j] = NULL;
      
          for (i=0; i < xlen; ++i)
              for (j=0; j < ylen; ++j)
                  if ((p[i][j] = malloc(zlen * sizeof *p[i][j])) == NULL) {
                      perror("malloc 3");
                      free_data(p, xlen, ylen);
                      return NULL;
                  }
      
          return p;
      }
      

      请注意,我已经相当简化了malloc 调用:一般来说,您不应该强制转换malloc 的返回值,而是将要分配的对象指定为sizeof 运算符的操作数它的类型。这使得malloc 调用更容易编写并且更不容易出错。您需要为malloc 包含stdlib.h

      这是一个使用以上两个函数的测试程序:

      #include <stdlib.h>
      #include <errno.h>
      #include <stdio.h>
      #include <time.h>
      
      int main(void)
      {
          int ***data;
          size_t xlen = 10;
          size_t ylen = 100;
          size_t zlen = 300;
          size_t i, j, k;
      
          srand((unsigned int)time(NULL));
          if ((data = alloc_data(xlen, ylen, zlen)) == NULL)
              return EXIT_FAILURE;
      
          for (i=0; i < xlen; ++i)
              for (j=0; j < ylen; ++j)
                  for (k=0; k < zlen; ++k)
                      data[i][j][k] = rand();
      
          printf("%d\n", data[1][2][1]);
          free_data(data, xlen, ylen);
          return EXIT_SUCCESS;
      }
      

      如果您发现这种方法更容易使用,请务必使用它。一般来说,这会比使用一块连续的内存要慢,但是如果你发现上面的方案速度还可以,如果它让你的生活更轻松,你可以继续使用它。即使您不使用它,也很高兴知道如何使这样的方案起作用。

      【讨论】:

      • 嗨,这可能很老了,但我想问一下:这个p = malloc(xlen * sizeof *p))是否相当于p = (int**)malloc(xlen * sizeof(int*));
      【解决方案6】:

      通过这种方式,您只能分配 1 个内存块,并且动态数组的行为类似于静态数组(即相同的内存连续性)。 您也可以像普通的一维数组一样使用单个 free(array) 来释放内存。

      double*** arr3dAlloc(const int ind1, const int ind2, const int ind3)
      {
        int i;
        int j;
        double*** array = (double***) malloc( (ind1 * sizeof(double*)) + (ind1*ind2 * sizeof(double**)) + (ind1*ind2*ind3 * sizeof(double)) );
        for(i = 0; i < ind1; ++i) {
          array[i] = (double**)(array + ind1) + i * ind2;
          for(j = 0; j < ind2; ++j) {
            array[i][j] = (double*)(array + ind1 + ind1*ind2) + i*ind2*ind3 + j*ind3;
          }
        }
        return array;
      }
      

      【讨论】:

        【解决方案7】:

        您确定需要使用malloc吗? C 允许本地创建多维数组:

        int a2[57][13][7];
        

        或者您可以通过以下方式使用malloc

        int (*a)[13][7]; // imitates 3d array with unset 3rd dimension
                         // actually it is a pointer to 2d arrays
        
        a = malloc(57 * sizeof *a);    // allocates 57 rows
        
        a[35][7][3] = 12; // accessing element is conventional
        
        free(a); // freeing memory
        

        【讨论】:

        • 另外我认为可以从malloc投,但不确定,需要检查...
        • 原生多维数组经常遇到内存限制
        • @polyphant C 为元素分配准确的空间,没有额外的数据
        • 栈上的变量是有大小限制的,而不是堆上的。见这里gribblelab.org/CBootcamp/7_Memory_Stack_vs_Heap.html
        • 对不起,误会你了,你当然是对的。
        【解决方案8】:
        #include<stdio.h>
        #include<stdlib.h>
        
        #define MAXX 3
        #define MAXY 4
        #define MAXZ 5
        
        main()
        {
            int ***p,i,j;
            p=(int ***) malloc(MAXX * sizeof(int **));
        
            for(i=0;i < MAXX;i++)
            {
                p[i]=(int **)malloc(MAXY * sizeof(int *));
                for(j=0;j < MAXY;j++)
                    p[i][j]=(int *)malloc(MAXZ * sizeof(int));
            }
        
            for(k=0;k < MAXZ;k++)
                for(i=0;i < MAXX;i++)
                    for(j=0;j < MAXY;j++)
                        p[i][j][k]= < something >;
        
        }
        

        【讨论】:

          【解决方案9】:

          希望对你有帮助!!!!

          在为 3D 数组中的 2D 数组分配内存时,将分配的内存分配给 array[i] 而不是 *array[i],这将不会出现段错误。

          这是你的程序

          int main () 
          {
              int ***array = malloc(3*sizeof(int**));
              int i, j;
          
              for (i = 0; i < 3; i++) {
                 array[i] = malloc(3*sizeof(int*));
                 for (j = 0; j < 3; j++) {
                    array[i][j] = malloc(3*sizeof(int));
                 }
              }
          
              array[1][2][1] = 10;
          
              return 0;
          }
          

          【讨论】:

            【解决方案10】:

            这应该可以,你没有对 malloc 的返回值进行类型转换

            #include <stdio.h>
            
            int main () {
              int ***array = (int ***) malloc(3*sizeof(int**));
              int i, j;
            
              for (i = 0; i < 3; i++) {
                array[i] = (int **)malloc(3*sizeof(int*));
                for (j = 0; j < 3; j++) {
                  array[i][j] = (int *)malloc(3*sizeof(int));
                }
              }
            
              array[1][2][1] = 10;
              printf("%d\n", array[1][2][1]);
              return 0;
            }
            

            工作链接:http://ideone.com/X2mcb8

            【讨论】:

              【解决方案11】:

              在 3d 内存分配代码下方:

              int row3d = 4;
              int column3d = 4;
              int height3d =4;
              int val3d =10;
              
              int ***arr3d = (int***)malloc (row3d*sizeof(int**));
              for (int i =0 ; i<column3d;i++)
              {
                  arr3d[i] = (int**)malloc (column3d*sizeof(int*));
                  for (int j = 0;j<height3d;j++)
                  {
                      arr3d[i][j] = (int*)malloc (height3d*sizeof(int));
              
                      for (int z =0;z<height3d;z++,val3d++)
                      {
                          arr3d[i][j][z]   = val3d;
                      }
                  }
              
              }
              // De allocation.
              for (int i=0;i<row3d;i++)
              {
                  for(int j=0;j<column3d;j++)
                  {
                      free(arr3d[i][j]);
                  }
              }
              free(arr3d);
              arr3d = 0;
              

              【讨论】:

                【解决方案12】:

                您强迫自己将其视为分配 3D 数组的两种根本不同的方式。 两个明确的区分细节强化了这种看法:1)第二种方法使用多个级别的间接来访问实际元素,2)第二种方法分配较低级别的一维数组独立

                但你究竟为什么坚持独立地分配较低级别的一维数组呢?你不必那样做。一旦考虑到这一点,您应该意识到还有第三种构建 3D 数组的方法

                int ***array3d = malloc(3 * sizeof(int **));
                int **array2d = malloc(3 * 3 * sizeof(int *));
                int *array1d = malloc(3 * 3 * 3 * sizeof(int));
                
                for (size_t i = 0; i < 3; i++) 
                {
                  array3d[i] = array2d + i * 3;
                  for (size_t j = 0; j < 3; j++)
                    array3d[i][j] = array1d + i * 3 * 3 + j * 3;
                }
                
                array[1][2][1] = 10;
                

                如果你仔细观察这个分配方法,你应该会发现这和你的第二种方法几乎是一样的:它通过在每个间接级别使用中间指针来构建一个三层数组结构。唯一的区别是它预先为每个间接级别连续预分配内存,“一次”,而不是进行多次重复的malloc 调用。随后的循环只是在子数组中分配预分配的内存(即它只是初始化指针)。

                但是,如果您仔细观察,您还会注意到实际的数组元素内存(存储实际值的 ints)的分配方式与您在第一种方法中的分配方式完全相同: malloc(3 * 3 * 3 * sizeof(int)); - 作为一个普通的平面连续数组。

                现在,如果您考虑一下,您应该意识到第三种方法与您的第一种方法没有太大区别。它们都使用大小为xSize * ySize * zSize 的平面数组来存储数据。这里唯一真正的区别是我们用来计算索引以访问该平面数据的方法。在第一种方法中,我们将即时计算索引为

                array1d[z * ySize * xSize + y * xSize + x]
                

                在第三种方法中,我们提前预先计算指向数组元素的指针,使用基本相同的公式,将预先计算的结果存储在其他数组中,稍后使用“自然”检索它们数组访问语法

                array3d[x][y][x]
                

                这里的问题是这种预先计算是否值得付出额外的努力和额外的内存。答案是:一般不,不是。通过花费这些额外的内存,您不会获得任何明显的性能优势(可能会使您的代码变慢)。

                您的第二种方法可能值得考虑的唯一情况是当您处理真正的锯齿状/锯齿状数组时:一个稀疏的多维数组,其中一些子数组部分丢失/未使用或有减小尺寸。例如,如果已知 3D 数组的某些 1D 或 2D 子数组只包含零,则您可能决定根本不将它们存储在内存中并将相应的指针设置为 null。这意味着使用您的第二种方法,其中子数组是独立分配(或不分配)的。如果数据很大,那么由此节省的内存可能是非常值得的。

                还请注意,当我们谈论具有 3 维或更多维的数组时,第一/第二/第三分配方法可以一起使用,同时用于不同级别的间接。您可能决定使用第一种方法实现 2D 数组,然后使用第二种方法将它们组合成 3D 数组。

                【讨论】:

                  【解决方案13】:

                  关于段错误, 我很确定其他人已经指出了这一点,但以防万一, 第一个for循环的第一行有一个额外的'*'

                  for (i = 0; i < 3; i++) {
                      *array[i] = malloc(3*sizeof(int*));
                  //  ^ we dont want to deference array twice
                      for (j = 0; j < 3; j++) {
                          array[i][j] = malloc(3*sizeof(int));
                      }
                  }
                  

                  尝试以下方法:

                      for (i = 0; i < 3; i++) {
                          array[i] = malloc(3*sizeof(int*));
                          for (j = 0; j < 3; j++) {
                              array[i][j] = malloc(3*sizeof(int));
                          }
                      }
                  

                  【讨论】:

                    【解决方案14】:

                    为了理解这个过程,我建议你先为p分配内存,然后为p[0],最后为p[0][0],这是一个简单的例子:

                       int ***a;
                         a  = (int***)malloc(sizeof(int**));
                        *a  = (int**)malloc(sizeof(int*));
                       **a  = (int*)malloc(sizeof(int));
                    

                    【讨论】:

                      【解决方案15】:

                      这是同样的事情,但它只有一次调用malloc

                      void* allocate_3d_matrix(size_t size, size_t r1, size_t r2, size_t r3) {
                          size_t malloc_size = r1 * sizeof(char**) + r1 * r2 * sizeof(char*) + r1 * r2 * r3 * size;
                          char*** p = malloc(malloc_size);
                      
                          char** r2_start = (char**) p + r1;  
                      
                          for (int i = 0; i < r1; i++) {
                              p[i] = r2_start + i * r2;
                          }
                      
                          char* r3_start = (char*) r2_start + (r1 * r2 * sizeof(char*));
                      
                          for (int i = 0; i < r1; i++) {
                              for (int j = 0; j < r2; j++) {
                                  p[i][j] = r3_start + size * (j + i * r2) * r3;
                              }
                          }
                      
                          return p;
                      }
                      
                      

                      删除矩阵只是free(p),因为所有内容都分配在一个块中。从性能的角度来看,它应该是最快的,因为内存块是最小的,它只有一次调用malloc(内存碎片更少)。

                      【讨论】:

                        猜你喜欢
                        • 2016-08-21
                        • 2021-02-21
                        • 1970-01-01
                        • 2013-05-25
                        • 2020-08-04
                        • 2016-12-03
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多