在一个大块中分配3D矩阵

时间:2015-03-02 20:04:42

标签: c malloc

我想在一个大块中分配一个3D矩阵。应该可以以[i][j][k]方式访问此矩阵,而无需每次都计算线性化索引。

我认为它应该像下面这样,但我在填写...

时遇到了麻烦
double ****matrix = (double ****) malloc(...)
for (int i = 0; i < imax; i++) {
    matrix[i] = &matrix[...]
    for (int j = 0; j < jmax; j++) {
        matrix[i][j] = &matrix[...]
            for (int k = 0; k < kmax; k++) {
                matrix[i][j][k] = &matrix[...]
            }
    }
}

4 个答案:

答案 0 :(得分:2)

这可以通过C中的一个简单malloc()调用来完成(不是在C ++中,但在C ++中没有可变长度数组):

void foo(int imax, int jmax, int kmax) {
    double (*matrix)[jmax][kmax] = malloc(imax*sizeof(*matrix));
    //Allocation done. Now fill the matrix:
    for(int i = 0; i < imax; i++) {
        for(int j = 0; j < jmax; j++) {
            for(int k = 0; k < kmax; k++) {
                matrix[i][j][k] = ...
            }
        }
    }
}

请注意,C允许jmaxkmax是仅在运行时已知的动态值。这就是C ++中缺少的能力,这使得C数组比C ++数据库强大得多。


正如WhozCraig正确指出的那样,这种方法的唯一缺点是,您无法使用void*将结果矩阵作为函数的返回值返回。但是,您可以通过引用返回它,如下所示:

void foo(int imax, int jmax, int kmax, double (**outMatrix)[jmax][kmax]) {
    *outMatrix = malloc(imax*sizeof(**outMatrix));
    double (*matrix)[jmax][kmax] = *outMatrix;    //avoid having to write (*outMatrix)[i][j][k] everywhere

    ...  //as above
}

需要像这样调用此函数:

int imax = ..., jmax = ..., kmax = ...;
double (*myMatrix)[jmax][kmax];
foo(imax, jmax, kmax, &myMatrix);

这样,即使它们是运行时值,您也可以对内部二维尺寸进行完整类型检查。

答案 1 :(得分:2)

要使单个分配成为可能并且有效,您需要像这样布局生成的内存:

  • imax double **
  • 的单位
  • imax * jmax double *
  • 的单位
  • imax * jmax * kmax double
  • 的单位

此外,必须首先分配'imax'double **个单位;你可以对其他两个部分重新排序,但按照列出的顺序处理它们是最明智的。

你还需要能够假设doubledouble *(以及double **,但这并不是一个很大的延伸),它们可以很好地对齐,你可以简单地分配块连续。对于类型为double的大多数64位系统,这将保持正常,但要注意它不能保留在32位系统或double以外的其他类型(基本上,当sizeof(double) != sizeof(double *))时,假设可能会有问题。

根据这些警告,这段代码干净利落(在Mac OS X 10.10.2上使用GCC 4.9.1和Valgrind版本valgrind-3.11.0.SVN进行测试):

#include <stdio.h>
#include <stdlib.h>

typedef double Element;

static Element ***alloc_3d_matrix(size_t imax, size_t jmax, size_t kmax)
{
    size_t i_size = imax * sizeof(Element **);
    size_t j_size = imax * jmax * sizeof(Element *);
    size_t k_size = imax * jmax * kmax * sizeof(Element);

    Element ***matrix = malloc(i_size + j_size + k_size);

    if (matrix == 0)
        return 0;

    printf("i = %zu, j = %zu, k = %zu; sizes: i = %zu, j = %zu, k = %zu; "
           "%zu bytes total\n",
           imax, jmax, kmax, i_size, j_size, k_size, i_size + j_size + k_size);
    printf("matrix          = %p .. %p\n", (void *)matrix,
           (void *)((char *)matrix + i_size + j_size + k_size));

    Element **j_base = (void *)((char *)matrix + imax * sizeof(Element **));
    printf("j_base = %p\n", (void *)j_base);
    for (size_t i = 0; i < imax; i++)
    {
        matrix[i] = &j_base[i * jmax];
        printf("matrix[%zu]       = %p (%p)\n",
               i, (void *)matrix[i], (void *)&matrix[i]);
    }

    Element *k_base = (void *)((char *)j_base + imax * jmax * sizeof(Element *));
    printf("k_base = %p\n", (void *)k_base);
    for (size_t i = 0; i < imax; i++)
    {
        for (size_t j = 0; j < jmax; j++)
        {
            matrix[i][j] = &k_base[(i * jmax + j) * kmax];
            printf("matrix[%zu][%zu]    = %p (%p)\n",
                   i, j, (void *)matrix[i][j], (void *)&matrix[i][j]);
        }
    }

    /* Diagnostic only */
    for (size_t i = 0; i < imax; i++)
    {
        for (size_t j = 0; j < jmax; j++)
        {
            for (size_t k = 0; k < kmax; k++)
                printf("matrix[%zu][%zu][%zu] = %p\n",
                       i, j, k, (void *)&matrix[i][j][k]);
        }
    }

    return matrix;
}

int main(void)
{
    size_t i_max = 3;
    size_t j_max = 4;
    size_t k_max = 5;

    Element ***matrix = alloc_3d_matrix(i_max, j_max, k_max);
    if (matrix == 0)
    {
        fprintf(stderr, "Failed to allocate matrix[%zu][%zu][%zu]\n", i_max, j_max, k_max);
        return 1;
    }

    for (size_t i = 0; i < i_max; i++)
    {
        for (size_t j = 0; j < j_max; j++)
        {
            for (size_t k = 0; k < k_max; k++)
                matrix[i][j][k] = (i + 1) * 100 + (j + 1) * 10 + k + 1;
        }
    }

    for (size_t i = 0; i < i_max; i++)
    {
        for (size_t j = 0; j < j_max; j++)
        {
            for (size_t k = k_max; k > 0; k--)
                printf("[%zu][%zu][%zu] = %6.0f\n", i, j, k-1, matrix[i][j][k-1]);
        }
    }

    free(matrix);
    return 0;
}

示例输出(省略了一些无聊的位):

i = 3, j = 4, k = 5; sizes: i = 24, j = 96, k = 480; 600 bytes total
matrix          = 0x100821630 .. 0x100821888
j_base = 0x100821648
matrix[0]       = 0x100821648 (0x100821630)
matrix[1]       = 0x100821668 (0x100821638)
matrix[2]       = 0x100821688 (0x100821640)
k_base = 0x1008216a8
matrix[0][0]    = 0x1008216a8 (0x100821648)
matrix[0][1]    = 0x1008216d0 (0x100821650)
matrix[0][2]    = 0x1008216f8 (0x100821658)
matrix[0][3]    = 0x100821720 (0x100821660)
matrix[1][0]    = 0x100821748 (0x100821668)
matrix[1][1]    = 0x100821770 (0x100821670)
matrix[1][2]    = 0x100821798 (0x100821678)
matrix[1][3]    = 0x1008217c0 (0x100821680)
matrix[2][0]    = 0x1008217e8 (0x100821688)
matrix[2][1]    = 0x100821810 (0x100821690)
matrix[2][2]    = 0x100821838 (0x100821698)
matrix[2][3]    = 0x100821860 (0x1008216a0)
matrix[0][0][0] = 0x1008216a8
matrix[0][0][1] = 0x1008216b0
matrix[0][0][2] = 0x1008216b8
matrix[0][0][3] = 0x1008216c0
matrix[0][0][4] = 0x1008216c8
matrix[0][1][0] = 0x1008216d0
matrix[0][1][1] = 0x1008216d8
matrix[0][1][2] = 0x1008216e0
matrix[0][1][3] = 0x1008216e8
matrix[0][1][4] = 0x1008216f0
matrix[0][2][0] = 0x1008216f8
…
matrix[2][2][4] = 0x100821858
matrix[2][3][0] = 0x100821860
matrix[2][3][1] = 0x100821868
matrix[2][3][2] = 0x100821870
matrix[2][3][3] = 0x100821878
matrix[2][3][4] = 0x100821880
[0][0][4] =    115
[0][0][3] =    114
[0][0][2] =    113
[0][0][1] =    112
[0][0][0] =    111
[0][1][4] =    125
[0][1][3] =    124
[0][1][2] =    123
[0][1][1] =    122
[0][1][0] =    121
[0][2][4] =    135
…
[2][2][0] =    331
[2][3][4] =    345
[2][3][3] =    344
[2][3][2] =    343
[2][3][1] =    342
[2][3][0] =    341

显示的代码中有很多诊断输出。

此代码适用于C89(以及C99和C11),无需支持可变长度数组或VLA - 但由于我在for循环中声明变量,因此编写的代码需要C99或更高版本,但可以很容易地修改它来声明for循环之外的变量,然后可以用C89编译。

答案 2 :(得分:0)

这对我有用:

void foo(int imax, int jmax, int kmax)
{
   // Allocate memory for all the numbers.
   // Think of this as (imax*jmax) number of memory chunks,
   // with each chunk containing kmax doubles.
   double* data_0 = malloc(imax*jmax*kmax*sizeof(double));

   // Allocate memory for the previus dimension of pointers.
   // This of this as imax number of memory chunks, 
   // with each chunk containing jmax double*.
   double** data_1 = malloc(imax*jmax*sizeof(double*));

   // Allocate memory for the previus dimension of pointers.
   double*** data_2 = malloc(imax*sizeof(double**));

   for (int i = 0; i < imax; i++)
   {
      data_2[i] = &data_1[i*jmax];
      for (int j = 0; j < jmax; j++)
      {
         data_1[i*jmax+j] = &data_0[(i*jmax+j)*kmax];
      }
   }

   // That is the matrix.
   double ***matrix = data_2;

   for (int i = 0; i < imax; i++)
   {
      for (int j = 0; j < jmax; j++)
      {
         for (int k = 0; k < kmax; k++)
         {
            matrix[i][j][k] = i+j+k;
         }
      }
   }

   for (int i = 0; i < imax; i++)
   {
      for (int j = 0; j < jmax; j++)
      {
         for (int k = 0; k < kmax; k++)
         {
            printf("%lf ", matrix[i][j][k]);
         }
         printf("\n");
      }
   }

   // Deallocate memory
   free(data_2);
   free(data_1);
   free(data_0);
}

答案 3 :(得分:0)

注意:这是一个评论,但它太长了,直到它变成一个正确的答案。


在不执行某些计算的情况下,您无法使用单个内存块。

请注意,每行的开头都用公式

标记
// row_begin is the memory address of the row at index row_idx
row_begin = row_idx * jmax * kmax

然后,每列取决于行的开始位置:

// column_begin is the memory address of the column 
// at index column_idx of the row starting at row_begin
column_begin = row_begin + column_idx * kmax

其中,使用绝对地址(当然相对于矩阵指针)转换为:

column_begin = (row_idx * jmax * kmax) + column_idx * kmax

最后,获取元素的k-index非常简单,遵循先前的规则,这可能会导致无限递归:

// element address = row_address + column_address + element_k_index
element_k_idx = column_begin + element_k_idx

转换为

element_k_idx = (row_idx * jmax * kmax) + column_idx * kmax + element_k_idx