动态创建连续的5D阵列?

时间:2016-09-02 18:20:45

标签: c arrays malloc heap contiguous

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

6 个答案:

答案 0 :(得分:4)

来自Jens Gustedt:Don't use fake matrices

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

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

只需一次免费电话即可释放内存。

free(matrix5d);

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

答案 1 :(得分: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中做这个(略有不同),但上面提到的变化取决于可变长度数组,这是C99中的新数据。

答案 2 :(得分:1)

考虑它的一种方法是使用malloc来分配1d数组的4d数组,因为从根本上malloc只能分配1d数组,而Nd数组只是1d数组(N -1)-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

更新:

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

这里使用typedef只是为了使指针到数组的语法更清晰一些。

但是,启用VLA后,int (*arr)[n][o][p][q] = malloc(m*sizeof(*arr));仍应有效,并允许您在每个维度上指定动态尺寸。

答案 3 :(得分:1)

有一种方法可以使记忆连续,但是它的优雅或凌乱是否会留给你;)

首先,让我们考虑一维数组的情况。在这种情况下,获得连续记忆是微不足道的;你从malloc获得的记忆将是连续的。这似乎很简单,但我们以后会使用这个事实来获得一个5维连续数组。

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

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是&#34;宽度&#34;数组(表示行中元素的数量)和N是&#34; height&#34;数组(表示数组中的行数)。另外,我们只是说数组中的第一个M条目是第一行,下一个M条目是第二行,等等。所以要读取行{{ 1}},列y,我们会这样做:

x

假设我们想要元素(0,0)。那么float data = array2d[y * M + x]; 只会变为0,所以我们很好。现在说我们想要元素(1,0)(即第二行中的第一个元素)。然后,y * M + x变为y * M + x,我们上面已经确定了第二行的开始位置。

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

z

从概念上讲,您可以将此视为跳过第一个float data = array3d[z * (M * N) + y * (M) + x]; 二维数组,然后跳过该数组的第一个z行,然后转到该数组的y元素行。对于更多维度,索引时有更多乘法项,但方法基本相同。

答案 4 :(得分:-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的2D数组。因此,例如,如果您需要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数组

答案 5 :(得分:-1)

如果我理解你的问题,你有一个当前的5D阵列,你需要为该阵列分配存储,并制作该阵列的副本,然后你希望以顺序的方式访问这些值。正如其他人所指出的那样,方法是使用指向4D数组的指针来分配一块内存dim1 * sizeof 4D来保存现有的数组。 (您可以考虑分配 dim1行构成您的5D阵列的内容)。然后,您可以简单地复制现有数组(使用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阵列中捕获的数据的任何内容,以便以其他格式输出数据。