【问题标题】:Dynamically creating a contiguous 5D array? [closed]动态创建一个连续的 5D 数组? [关闭]
【发布时间】:2017-01-10 22:28:39
【问题描述】:

我正在处理一个非常大的 5D 数组,我需要将其读入连续内存(另一个 5D 数组)。我无法将数组放在堆栈上,因为它太大并且会产生段错误。我所做的是使用 malloc 动态创建一个 5D 数组,但是我发现它不是连续的内存。是否有一个优雅的解决方案,或者无论如何都会变得一团糟?

【问题讨论】:

  • 显示相关代码。
  • 它变得凌乱,例如:eli.thegreenplace.net/2015/…
  • 不是将变量设为 5d 数组,而是将其设为指向 5d 数组的指针,然后将内存分配给该指针。在这种情况下,可以像分配任何其他对象一样分配数组
  • 你知道 5d 数组只是一个设计错误,不是吗?除非它代表一个实际的数学对象,否则会想到张量。但是,在理解您试图解决的潜在问题时,考虑这样的数组似乎是一个问题。

标签: arrays c malloc heap-memory contiguous


【解决方案1】:

如果我理解您的问题,您有一个当前的 5D 数组,您需要为该数组分配存储空间并制作该数组的副本,然后您希望以顺序方式访问这些值。正如其他人所指出的,该方法是使用 指向 4D 数组的指针 分配一块内存 dim1 * sizeof 4D 来保存现有数组。 (您可以考虑为构成 5D 数组的 dim1 行 分配)。然后,您可以简单地复制现有数组(使用memcpy 等),然后分配一个指向第一个元素的指针以进行顺序访问。

好处是您可以分配一个块来保存现有数组的副本。使用完副本后,这将只需要一个 free

这不适用于伪造的(指向指针的指针...内存集合)

下面是创建dim1 指针的简短示例,该指针指向构成现有数组的剩余4d 的内容(在单个块分配中),其中除了dim1 之外的所有维度在编译时都是已知的。现有的 5D 数组 a 被复制到分配给 b 的新内存块中。然后将整数指针“p”分配给b 的起始地址。 b的值是通过指针p顺序访问的。

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

int main (void) {

    int a[2][2][2][2][2] = { { { {{1,2}, {3,4}},        /* 5D Array */
                                 {{5,6}, {7,8}} },
                               { {{1,2}, {3,4}},
                                 {{5,6}, {7,8}} } },
                             { { {{1,2}, {3,4}},
                                 {{5,6}, {7,8}} },
                               { {{1,2}, {3,4}},
                                 {{5,6}, {7,8}} } } };
    /* ptr to 5D, ptr to int* */
    int (*b)[2][2][2][2] = NULL, *p = NULL;

    /* allocate block to hold a */
    b = malloc (sizeof a/sizeof *a * sizeof *b);
    memcpy (b, a, sizeof a/sizeof *a * sizeof *b);  /* copy a to b */

    p = ****b;                 /* assign address of first element */
    printf ("\nb:\n");          /* ouput using sequential access */
    for (int i = 0; i < (int)(sizeof a/sizeof *****a); i++)
        printf (" *(p + %2d) : %d\n", i, p[i]);

    free (b);   /* single free is all that is required */

    return 0;
}

使用/输出示例

$ ./bin/arr5dstatic1

b:
 *(p +  0) : 1
 *(p +  1) : 2
 *(p +  2) : 3
 *(p +  3) : 4
 *(p +  4) : 5
 *(p +  5) : 6
 *(p +  6) : 7
 *(p +  7) : 8
 *(p +  8) : 1
 *(p +  9) : 2
 *(p + 10) : 3
 *(p + 11) : 4
 *(p + 12) : 5
 *(p + 13) : 6
 *(p + 14) : 7
 *(p + 15) : 8
 *(p + 16) : 1
 *(p + 17) : 2
 *(p + 18) : 3
 *(p + 19) : 4
 *(p + 20) : 5
 *(p + 21) : 6
 *(p + 22) : 7
 *(p + 23) : 8
 *(p + 24) : 1
 *(p + 25) : 2
 *(p + 26) : 3
 *(p + 27) : 4
 *(p + 28) : 5
 *(p + 29) : 6
 *(p + 30) : 7
 *(p + 31) : 8

其余的 cmets 和答案建议您找到使用 5D 阵列设置以外的其他方法,这是有充分理由的。调查一下是否可以修改生成原始 5D 数组中捕获的数据的任何内容,以便以其他格式输出数据。

【讨论】:

    【解决方案2】:

    来自 Jens Gustedt:Don't use fake matrices

    分配一个维度为 A x B x C x D x E 的 5 维矩阵(在编译时不需要知道维度),如下所示:

    float (*matrix5d)[B][C][D][E] = malloc(sizeof(float[A][B][C][D][E]));
    

    通过一次 free 调用释放内存。

    free(matrix5d);
    

    请注意,对于可变长度数组,以上要求 C99 或更高版本。

    【讨论】:

    • 如何访问偏移量?对于操作matrix[i][0] = i;,当矩阵定义为int (*matrix)[s1_len+1][s2_len+1] 时,我得到`错误:从类型'size_t''分配给类型'int [(sizetype)s2_len + 1u]'时不兼容的类型。
    • 严格来说,它需要 C99 实现(所有 C99 实现都需要支持 VLA)或未定义 __STDC_NO_VLA__ 的 C11 实现(因为 VLA 支持在 C11 中是可选的)。
    • @TomášZato 您需要第三个索引来完成作业:matrix[i][0][0] = i
    • @thndrwrks 感谢您的回复。我改用(*matrix)[i][0] = i,我想这是等价的。我觉得它更直观。
    【解决方案3】:

    通过连续的内存块表示是 C 数组的显着属性之一。多维数组是数组的数组,因此与任何其他数组一样是连续的,所以如果你想要一个真正的 5D 数组,那么你当然需要连续的内存。正如其他一些答案所观察到的那样,为确保获得连续的内存块,您必须一次分配整个内存。

    虽然您可以形成由指向 [[指向 [指向...的指针的数组]] 数组的指针数组组成的数据结构,但它们根本不是一回事,就像指针不是数组一样。您可以将索引运算符 [] 用于此类数据结构,就像使用多维数组一样,但这并不意味着它们是一回事。

    @EvelynParenteau 建议使用 1D 数组模拟您的 5D 数组,这确实是满足您的连续性要求的一种方法。您甚至可以编写宏来简化对此类数组的索引。

    但只要你至少使用C99,你就可以动态分配一个真正的5D数组。一般形式可能如下所示:

    void allocate_5d(unsigned dim1, unsigned dim2, unsigned dim3, unsigned dim4,
            unsigned dim5, double (**aptr)[dim2][dim3][dim4][dim5]) {
        *aptr = malloc(dim1 * sizeof(**aptr));
    }
    

    它会这样使用:

    void do_something(unsigned dim1, unsigned dim2, unsigned dim3, unsigned dim4,
            unsigned dim5) {
        double (*array)[dim2][dim3][dim4][dim5];
    
        allocate_5d(dim1, dim2, dim4, dim4, dim5, &array);
    
        if (!array) {
            // Handle allocation failure ...
        }
    
        array[0][0][0][0][0] = 42;
        // ...
    
        free(array);
    }
    

    如果维度 2 - 5 是编译时常量,那么您甚至可以在 C90 中执行 this(略有不同),但上述变化取决于可变长度数组,这是 C99 中的新功能。

    【讨论】:

      【解决方案4】:

      一种思考方式是使用malloc分配4d数组的1d数组,因为从根本上malloc只能分配1d数组,而Nd数组只是(N-1)的1d数组- d 数组。

      但是,就像malloc 分配的任何数组一样,“数组对象”实际上是一个指针,因此您不应该使用sizeof() 来获取数组的大小。

      #include <stdio.h>
      #include <stdlib.h>
      typedef int Array_4D_Type[4][3][2][1];
      int main(void) {
          Array_4D_Type *arr = malloc(5 * sizeof(Array_4D_Type));
         // ^^^^^^^^^^^^^^^^ here, allocate a length-5 vector of 4d array type
          int *p = &arr[0][0][0][0][0];
          for (int i = 0 ; i < 120 ; i++){
              p[i] = i;
          }
          printf("arr_start = %d, end = %d\n", arr[0][0][0][0][0], arr[4][3][2][1][0]);
      
          return 0;
      }
      

      You can test the code here.

      更新:

      正如 cmets 中提到的,在此处使用 typedef 会强制数组的大小为静态大小,除了顶部维度。

      这里使用typedef 只是为了使指向数组的语法更简洁一些。

      但是,在启用 VLA 的情况下,int (*arr)[n][o][p][q] = malloc(m*sizeof(*arr)); 应该仍然可以工作,并允许您在每个维度上指定动态大小。

      【讨论】:

      • 维度[1] 当然是毫无意义的。更严重的是,您如何修改此代码以提供int arr1[6][5][4][3][2]int arr2[2][3][4][5][6]?使用您的 typedef,您只能处理固定 4-D 数组类型的任意大小的数组,而不是任意 4-D 数组类型的任意大小的数组。
      • @JonathanLeffler int (*arr)[5][4][3][2] = malloc(6*sizeof(*arr)); ideone.com/mjv9GQ 正如你提到的typedef 确实需要一个恒定的大小,我并没有真正考虑使用 VLA 来完成动态大小。我选择4+1 D的原因
      • @JonathanLeffler 是因为我觉得它不需要像“指向 5d”方法中那样的(多余的)顶级尊重。
      【解决方案5】:

      有一种方法可以让记忆连续,但它是优雅还是混乱我会留给你;)

      首先,让我们考虑一维数组的情况。在这种情况下,获得连续内存是微不足道的。您从malloc 获得的内存将是连续的。看起来很简单,但是我们稍后会使用这个事实来获得一个 5 维的连续数组。

      现在,让我们考虑一个大小为M 乘以N 的二维数组。这是一种创建方法(假设我们使用的是floats)。

      float** array2d = malloc(M * sizeof(float*));
      for (int i = 0; i < M; i++) {
        array2d[i] = malloc(N * sizeof(float));
      }
      

      严格来说,这不是二维数组,它是数组的数组。现在,我们可以访问array2d 的元素,例如array2d[0][0]array2d[0][1] 等。从概念上讲,这非常好,但正如您所指出的,我们不一定有连续的内存,因为我们多次调用malloc .我们需要一种方法来分配存储 M*N 浮点所需的所有内存,一次调用 malloc

      float* array2d = malloc(M * N * sizeof(float));
      

      请注意,在这种形式中,array2dfloat* 而不是 float**,即它是浮点数数组,而不是浮点数数组。所以,我们不能再做array2d[0][0]了。我们现在如何索引这个数组?

      这完全由我们决定如何在内存中布置这个二维数组。假设M 是数组的“宽度”(表示一行中的元素数),N 是数组的“高度”(表示数组中的行数)。另外,假设数组中的第一个 M 条目是第一行,接下来的 M 条目是第二行,依此类推。所以要读取行 yx 的条目,我们会这样做:

      float data = array2d[y * M + x];
      

      假设我们想要元素 (0, 0)。然后y * M + x 简单地变为0,所以我们很好。现在说我们想要元素 (1, 0) (即第二行中的第一个元素)。然后,y * M + x 变为 M,正如我们在上面确定的那样,这就是第二行的开始位置。

      我们可以将这种方法推广到更高的维度。假设我们有一个大小为L by M by N 的三维数组。您可以将其视为L 在内存中按顺序排列的二维数组,大小均为M by N。然后,要访问元素(xyz),我们会这样做:

      float data = array3d[z * (M * N) + y * (M) + x];
      

      从概念上讲,您可以将其视为跳过第一个z 二维数组,然后跳过该数组的第一个y 行,然后转到该行的xth 元素。对于更多的维度,索引时有更多的乘法项,但方法基本相同。

      【讨论】:

      • 这在很大程度上是正确的,但是第一个分配示例没有分配一个二维数组。指针数组与二维数组完全不同。
      • 正确。我把第一个例子放在那里作为一个不会满足要求的例子。我将对其进行编辑以使其更清晰。
      【解决方案6】:

      动态分配,使用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);
      

      这会分配一个大小为dimension1_max * dimension2_max 的二维数组。因此,例如,如果您想要一个 640*480 数组(图像的 fe 像素),请使用 dimension1_max = 640, dimension2_max = 480。然后您可以使用 x[d1]​​[d2] 访问该数组,其中 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);
      

      以类似的方式,您可以动态构建 5d 数组

      【讨论】:

      • 我删除了 snippet 部分,C 不是 javascript 并且 sn-ps 在答案中没有意义。
      • 注意: 您也可以简单地使用 sizeof 运算符对变量本身的取消引用,例如x = malloc(dimension1_max * sizeof *x); 和类似的x[i] = malloc(dimension2_max * sizeof **x);(或*(x[i]))它可以防止在sizeof 中包含不正确的类型。
      • 你呈现的分配二维数组,数据保证满足OP的连续性要求。相反,您分配了一堆单独的一维数组——它们可能彼此相邻,也可能不相邻——以及指向这些数组的指针数组。这根本不是一回事。
      猜你喜欢
      • 1970-01-01
      • 2016-02-25
      • 2017-08-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多