0

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

4

6 回答 6

4

来自 Jens Gustedt:不要使用假矩阵

分配一个维度为 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 或更高版本。

于 2016-09-02T22:23:47.413 回答
2

通过连续的内存块表示是 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 中的新内容。

于 2016-09-02T19:46:16.787 回答
1

有一种方法可以使记忆连续,但无论是优雅还是凌乱,我都会留给你;)

首先,让我们考虑一维数组的情况。在这种情况下,获得连续内存是微不足道的。您从中获得的内存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现在,我们可以访问likearray2d[0][0]等元素array2d[0][1]。从概念上讲,这非常好,但正如您所指出的,我们不一定有连续的内存,因为我们多次调用malloc. 我们需要的是一种M*N在一次调用中分配存储浮点数所需的所有内存的方法malloc

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

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

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

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

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

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

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

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

于 2016-09-02T18:53:38.430 回答
1

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

但是,就像任何由 分配的数组一样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;
}

您可以在此处测试代码

更新:

正如评论中提到的,在typedef这里使用强制数组是静态大小的,除了顶部维度。

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

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

于 2016-09-02T18:45:33.023 回答
-1

通过动态分配,使用 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 question

请注意,在这种情况下,您的数组不会分配连续的内存区域(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 数组

于 2016-09-02T18:42:16.473 回答
-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

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

于 2016-09-03T00:12:19.533 回答