【问题标题】:Using malloc for allocation of multi-dimensional arrays with different row lengths使用malloc分配不同行长的多维数组
【发布时间】:2020-06-06 12:40:10
【问题描述】:

我有以下C 代码:

int *a;
size_t size = 2000*sizeof(int);
a = malloc(size);

效果很好。但如果我有以下情况:

char **b = malloc(2000*sizeof *b);

b 的每个元素都有不同的长度。

怎么可能为b 做同样的事情,就像我为a 做的一样;即以下代码是否正确?

char *c;
size_t size = 2000*sizeof(char *);
c = malloc(size);

【问题讨论】:

    标签: c arrays malloc


    【解决方案1】:

    首先,您需要分配像char **c = malloc( N * sizeof( char* )) 这样的指针数组,然后通过单独调用malloc 分配每一行,可能在循环中:

    
    /* N is the number of rows  */
    /* note: c is char** */
    if (( c = malloc( N*sizeof( char* ))) == NULL )
    { /* error */ }
    
    for ( i = 0; i < N; i++ )
    {
      /* x_i here is the size of given row, no need to
       * multiply by sizeof( char ), it's always 1
       */
      if (( c[i] = malloc( x_i )) == NULL )
      { /* error */ }
    
      /* probably init the row here */
    }
    
    /* access matrix elements: c[i] give you a pointer
     * to the row array, c[i][j] indexes an element
     */
    c[i][j] = 'a';
    

    如果您知道元素的总数(例如N*M),您可以在一次分配中完成此操作。

    【讨论】:

    • 如果在一次操作中分配 NM 个字节,则手动填充所有 c[i]:c[i] = p + Mi;跨度>
    • 这取决于 c 的类型 - 如果是 char**,那么是的,如果是 char*,那么索引会改变: element[i][j] ~ c[i*M + j] .
    • @Nikolai N Fetissov,代码中有很多malloc,怎么能全部释放?也使用 for 循环?
    • @e19293001 是的,每个malloc 一个free。您必须循环释放它们的 char* 变量,然后释放 char**
    • 我在一本书上看到过同样的情况,上面写着“......内存不能保证是连续的。”?
    【解决方案2】:

    动态分配 T 类型的 NxM 数组的典型形式是

    T **a = malloc(sizeof *a * N);
    if (a)
    {
      for (i = 0; i < N; i++)
      {
        a[i] = malloc(sizeof *a[i] * M);
      }
    }
    

    如果数组的每个元素都有不同的长度,则将 M 替换为该元素的适当长度;例如

    T **a = malloc(sizeof *a * N);
    if (a)
    {
      for (i = 0; i < N; i++)
      {
        a[i] = malloc(sizeof *a[i] * length_for_this_element);
      }
    }
    

    【讨论】:

    • 如果我有我将拥有的 int 总数,但没有进入每个数组的 int 总数,我应该如何进行?
    • 非常明确的答案,谢谢!您是否还可以添加说明以正确free分配的内存的顺序?
    • @Kagaratsch:通常,按照您分配的相反顺序释放 - 即首先释放每个 a[i],然后释放 a
    【解决方案3】:

    char a[10][20] 的等效内存分配如下。

    char **a;
    
    a=malloc(10*sizeof(char *));
    
    for(i=0;i<10;i++)
        a[i]=malloc(20*sizeof(char));
    

    我希望这看起来很容易理解。

    【讨论】:

      【解决方案4】:

      另一种方法是分配一个连续的内存块,包括用于指向行的指针的标题块以及用于将实际数据存储在行中的主体块。然后只需通过在每行基础上将正文中的内存地址分配给标题中的指针来标记内存。如下所示:

      int** 2dAlloc(int rows, int* columns) {    
          int header = rows * sizeof(int*);
      
          int body = 0;
          for(int i=0; i<rows; body+=columnSizes[i++]) {  
          }
          body*=sizeof(int);
      
          int** rowptr = (int**)malloc(header + body);
      
          int* buf  = (int*)(rowptr + rows);
          rowptr[0] = buf;
          int k;
          for(k = 1; k < rows; ++k) {
              rowptr[k] = rowptr[k-1] + columns[k-1];
          }
          return rowptr;
      }
      
      int main() {
          // specifying column amount on per-row basis
          int columns[] = {1,2,3};
          int rows = sizeof(columns)/sizeof(int);
          int** matrix = 2dAlloc(rows, &columns);
      
          // using allocated array
          for(int i = 0; i<rows; ++i) {
              for(int j = 0; j<columns[i]; ++j) {
                  cout<<matrix[i][j]<<", ";
              }   
                  cout<<endl;
          }
      
          // now it is time to get rid of allocated 
          // memory in only one call to "free"
          free matrix;
      }
      

      这种方法的优点是优雅地释放内存并能够使用类似数组的符号来访问生成的二维数组的元素。

      【讨论】:

      • 需要注意的是:这种解决方案通常会在缓存一致性方面表现得更好,因为可以保证各个行是连续的,这与其他一次分配一行的方法不同,并且可能导致到一个矩阵,其组成部分分散在一个高度碎片化的堆中。
      • 不幸的是,这也有不保证非指针大小类型对齐的副作用。例如:具有 32 位指针和具有奇数行的 64 位双精度的系统将在 double 的未对齐边界上开始双精度行的第一列。考虑到这一点非常很重要,因为不正确的数据对齐很容易导致总线错误。一个通用的解决方案应该确保数据行从 8 字节边界开始,在将行指针分配给主指针段时做出额外的分配空间并进行相应调整。
      • @DmitryAleks :你在哪里声明columnSizes[]
      【解决方案5】:

      如果 b 中的每个元素都有不同的长度,那么您需要执行以下操作:

      int totalLength = 0;
      for_every_element_in_b {
          totalLength += length_of_this_b_in_bytes;
      }
      return malloc(totalLength);
      

      【讨论】:

      • 它不会为 char* 指针的一维数组分配内存。
      【解决方案6】:

      我认为两步方法是最好的,因为 c 二维数组只是数组的数组。第一步是分配一个数组,然后循环遍历它,为每一列分配数组。 This article 提供了很好的细节。

      【讨论】:

        【解决方案7】:

        二维数组动态内存分配

        int **a,i;
        
        // for any number of rows & columns this will work
        a = malloc(rows*sizeof(int *));
        for(i=0;i<rows;i++)
            *(a+i) = malloc(cols*sizeof(int));
        

        【讨论】:

          【解决方案8】:

          malloc 不会在特定的边界上分配,因此必须假定它是在字节边界上分配的。

          如果转换为任何其他类型,则返回的指针将无法使用,因为访问该指针可能会导致 CPU 违反内存访问,并且应用程序将立即关闭。

          【讨论】:

            猜你喜欢
            • 2021-11-13
            • 2021-05-13
            • 2013-08-21
            • 2021-03-05
            • 2020-08-04
            • 2019-06-07
            • 1970-01-01
            • 2019-01-26
            相关资源
            最近更新 更多