堆上的动态多维数组

时间:2014-09-27 13:47:53

标签: c arrays multidimensional-array heap

我想创建一个函数,它可以在堆上分配一个多维数组,只需要调用一次malloc。 (指针数组)所以函数调用看起来像这样:

size_t dim[2]  = {2, 4};
int **_2darray = alloc_array(sizeof(int), dim, 2);
// ^ should be the "same" as:
int __2darray[2][4];

到目前为止我所拥有的是保存数组和指针所需的整个块的SIZE计算:

void *alloc_array(size_t element_size, size_t dimensions[static 1], size_t ndims)
{
    unsigned char *DATA = NULL;
    size_t SIZE         = 0;
    size_t multiplicators[ndims];

    // Calculate for each dimension the multiplier 
    // SIZE 3d array:  (N1 * sizeof(T **) + (N1 * N2 + sizeof(T *) + (N1 * N2 * n3 + sizeof(T))
    //                  ^- first mulitplier ^ second multiplier      ^ third multiplier

    for (size_t i = 0;  i < ndims; ++i) {
        multiplicators[i] = dimensions[i];

        for (size_t j = 0; j < i; ++j) {
            multiplicators[i] *= dimensions[j];
        }
    }

    SIZE = 0;
    for (size_t dimI = 0; dimI < ndims; ++dimI) {
        size_t mulval = multiplicators[dimI];

        // The elements are in the "last" dimension
        if (dimI+1 == ndims) {
            SIZE += element_size * mulval;
        } else {
            // All other elements are pointers to the specific element
            SIZE += sizeof(void *) * mulval;
        }
    }

    DATA = malloc(SIZE);
    return DATA;
}

所以现在SIZE计算有效。但现在我坚持设置指向正确元素的指针。我知道处理静态尺寸很容易,但我希望用动态尺寸来完成。

1 个答案:

答案 0 :(得分:2)

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

void fill_array_pointers (void** pointers, char* elements, 
                  size_t element_size, size_t total_elements_size, 
                  size_t dimensions[], size_t ndims)
{
    if (ndims == 2)
    {
        size_t i;
        for (i = 0; i < dimensions[0]; ++i)
        {
            pointers[i] = elements + i * element_size * dimensions[1];
        }
    }
    else
    {
        size_t i;
        size_t block_size = total_elements_size / dimensions[0];
        for (i = 0; i < dimensions[0]; ++i)
        {
            pointers[i] = pointers + dimensions[0] + i * dimensions[1];
            fill_array_pointers (pointers + dimensions[0] 
                                          + i * dimensions[1], 
                                  elements + block_size * i, 
                                  element_size, block_size, 
                                  dimensions+1, ndims-1);
        }
    }
}

void* alloc_array (size_t element_size, size_t dimensions[], 
                    size_t ndims)
{
    size_t total_elements_size = element_size;
    int i;

    // total size of elements

    for (i = 0; i < ndims; ++i)
        total_elements_size *= dimensions[i];

    // total size of pointers

    size_t total_pointers_size = 0;
    int mulval = 1;
    for (i = 0; i < ndims-1; ++i)
    {
        total_pointers_size += dimensions[i] * sizeof(void*) * mulval;
        mulval *= dimensions[i];
    }

    size_t total_size = total_pointers_size;
    size_t oddball = total_pointers_size % element_size; 
                // really needs to be alignof but we don't have it
    if (oddball) total_size += (element_size - oddball);
    total_size += total_elements_size;

    void* block = malloc (total_size);
    void** pointers = block;
    char* elements = (char*)block + total_size - total_elements_size;

    fill_array_pointers (pointers, elements, element_size,
                         total_elements_size, dimensions, ndims);
    return block;
}

试驾:

int main ()
{
    size_t dims[] = { 2, 3, 4 };
    int*** arr = alloc_array(sizeof(int), dims, 3);


    int i, j, k;
    for (i = 0; i < dims[0]; ++i)
        for (j = 0; j < dims[1]; ++j)
            for (k = 0; k < dims[2]; ++k)
            {
                arr[i][j][k] = i*100+j*10+k;
            }

    for (i = 0; i < dims[0]*dims[1]*dims[2]; ++i)
    {
        printf ("%03d ", (&arr[0][0][0])[i]);
    }
    printf ("\n");

    free (arr);
}

这对于char的系统上的多维sizeof(char*) != sizeof(char**)数组不起作用;这种系统存在但很少见。无论如何,多维char数组都是毫无意义的。

测试在valgrind下完全运行。

这更像是一种智力活动。如果您需要最大性能,请不要使用指针数组,使用平面数组和丑陋但有效的显式索引计算。如果您需要简洁明了的代码,最好分别分配每个级别。