3

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.

Zelphir Kaltstahl
  • 5,722
  • 10
  • 57
  • 86
  • 4
    You have too many restrictions - therefore this is impossible. – Ed Heal May 23 '15 at 21:20
  • 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 May 23 '15 at 21:24
  • 2
    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 May 23 '15 at 21:24
  • They are requirements of someone who does not understand c, you can create the arrays with the layout you want, you just can't access the data the way you want, maybe you should use c++? – Iharob Al Asimi May 23 '15 at 21:24
  • I am not trying to do anything with Fortran actually. Which of the requirements are in conflict? – Zelphir Kaltstahl May 23 '15 at 21:25
  • `Type (*a)[x] = malloc(y*sizeof(*a));` – BLUEPIXY May 23 '15 at 21:25
  • If you do `double *x = malloc(rows * columns * sizeof(*x));` then you will have the layout but you won't be able to do `x[i][j]`. – Iharob Al Asimi May 23 '15 at 21:26
  • 1. No pointers. 2. No predefined size of a row. 3. How can you use memory linearly? – Ed Heal May 23 '15 at 21:26
  • @iharob - But clause 2 says no predefined size of the array – Ed Heal May 23 '15 at 21:27
  • 2
    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 May 23 '15 at 21:27
  • I don't understand why one dimension must be known at compile time. Why can't the program while it is running simply choose the layout in memory depending on 2 dynamic variables? Why is it necessary to know the size of one row of the array while compiling? Can you explain in depth please? – Zelphir Kaltstahl May 23 '15 at 21:38
  • @Zelphir you can do all that in c++, in c you don't have to. – Iharob Al Asimi May 23 '15 at 21:40
  • "return the allocated array", what would be the return type of your function? – ryanpattison May 23 '15 at 21:46
  • 1
    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 May 23 '15 at 22:10
  • See my answer [here](http://stackoverflow.com/questions/30023867/how-can-i-work-with-dynamically-allocated-arbitrary-dimensional-arrays/30023868#30023868) for using a struct as an array header, or my answer [here](http://stackoverflow.com/questions/30409991/use-a-dope-vector-to-access-arbitrary-slices-of-a-multidimensional-array/30409992#30409992) for a more powerful data structure which allows slicing and transposing. – luser droog May 24 '15 at 00:09

2 Answers2

5

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.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • good: `double (*A)[rows][cols] = malloc(sizeof *A); *A[1][2] = 100.0; printf("%lf\n" , *A[1][2]);` – houssam May 23 '15 at 23:18
  • This seems to work fine! I have a question though: When I use: `double *A[n][m] = malloc(sizeof *A);` I get the error: `error: variable-sized object may not be initialized` However, when I don't write the brackets when printing it, it is ok. Why is that? – Zelphir Kaltstahl May 24 '15 at 09:32
  • 1
    @Zelphir, because these are two completely different animals. The one that gives you the error is a 2D table of pointers, whereas the correct one is a pointer to a 2D table. – Jens Gustedt May 24 '15 at 17:03
  • It is because in one case the * is grouped/attached to the variable name and dereferences A and in the other case it is grouped/attached with the type and it makes it a pointer type -- right? – Zelphir Kaltstahl May 25 '15 at 09:54
  • @Zelphir, yes, exactly. – Jens Gustedt May 25 '15 at 16:15
1

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
houssam
  • 1,823
  • 15
  • 27