3
votes

I have a simple question, but the answer seems to be very difficult to find:

How do I create a true 2D array in C (not C++), dynamically sized (size not known at compile time), not an array of pointers, on the heap, so that I can put that allocation into a separate function and return the allocated array, without receiving any warnings from gcc -Wall?

I've found numerous other questions here on SO and in other forums, but the answers all had some flaw:

  • I saw many answers, which showed how to initialize an array of pointers, which according to some comments can lead to memory fragmentation and needs a loop to be freed when not used anymore.
  • I don't want to only have a predefined size of the array, but want to use it in some loops, creating arrays of many sizes.
  • I don't want the values in the array to be predefined either, they're calculated while the program is running.
  • I read about the layout in the memory, which can be different when using some methods of creating the array, while one can still use the array like this: a[y][x]. I want my array to have the memory layout of a true 2D array as well.

What is the right way to achieve allocation of such a true 2D array?

EDIT#1: The return type of the allocation method can be a pointer to the allocated array.

2
You have too many restrictions - therefore this is impossible. - Ed Heal
I can't believe this cannot be done in C o.0 I mean these are pretty normal requirements. I simply want to do it propperly. Maybe you can tell which of my requirements makes it impossible? - Zelphir Kaltstahl
What you are trying to do makes no sense, you should apparently use fortran or a language more suited to what you want, because I suspect that you are worrying about passing the data to fortran. Also as @EdHeal said, if you do it with one of your restrictions, then it's impossible to apply the other, simply like that it's not possible. - Iharob Al Asimi
If you want an array of arrays, you need to know at least one dimension at compile-time. This is necessary or the compiler wouldn't know where &array[x][y] is. (To compute the address array + x, sizeof(array[0]) must be known). - molbdnilo
In standard C, the closest you can get is to create a struct with number of rows, number of columns, and a pointer to an allocated 1D array, and functions (possibly macros) to use the 1D array as a 2D array. - rcgldr

2 Answers

5
votes

You don't need a special function. Just do it like this

double (*A)[n][m] = malloc(sizeof *A);

As of C99, here n and m can be any positive integer expressions you want.

Such a thing is a pointer to a VLA, variable length array.

1
votes

I know that this is not the perfect answer; but I hope it will be helpful.

#include <stdio.h>
#include <stdlib.h>
#define ELEM(myArr,X,Y) (myArr->arr[myArr->cols * X + Y])
#define FREE_MY_ARR(myArr) \
    if(myArr){if(myArr->arr) free(myArr->arr);  free(myArr);}
    typedef struct myArr
    {
        int rows , cols;
        int * arr;
    } myArr;

    myArr * create_my_arr(int rows , int cols)
    {
        myArr * my_arr = malloc(sizeof(myArr));
        my_arr->rows =rows;
        my_arr->cols=cols;
        my_arr->arr= malloc(rows * cols * sizeof(*my_arr->arr));
        return my_arr;
    }

    int main()
    {
        int rows = 4 , cols = 5;
        myArr * my_arr = create_my_arr(4,5);
        int i , j ;
        for(i = 0 ; i < rows;i++)
            for(j = 0 ; j < cols;j++)
            {
                ELEM(my_arr , i , j) = cols * i + j; // 0,1,2,3,4,5,6,7,8,...etc
            }
        //print array.
        for(i = 0 ; i < rows;i++)
        {
            for(j = 0 ; j < cols;j++)
            {
                printf("arr[%d,%d]=%d\t" , i , j , ELEM(my_arr ,i , j));
            }
        printf("\n");
        }
        FREE_MY_ARR(my_arr);
       return 0;
    }

output:

   gcc -o s s.c &&s
    arr[0,0]=0      arr[0,1]=1      arr[0,2]=2      arr[0,3]=3      arr[0,4]=4
    arr[1,0]=5      arr[1,1]=6      arr[1,2]=7      arr[1,3]=8      arr[1,4]=9
    arr[2,0]=10     arr[2,1]=11     arr[2,2]=12     arr[2,3]=13     arr[2,4]=14
    arr[3,0]=15     arr[3,1]=16     arr[3,2]=17     arr[3,3]=18     arr[3,4]=19