Search code examples
cdynamicallocationcontiguous

Declare global contiguous 2d array at runtime. The dimensions are unknown at compile time C


I want to declare a global 2d array in C, and allocate contiguous memory at runtime because the minor dimension is unknown at compile time. I would like to dereference the array with 2-indices notation A[i][j]. If the array wasn't global c99 notation "double A[m][n]" would be handy but in my case does not apply. What would be the right strategy?

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

//TO DO
//DECLARE array here

void fun1() {
    array[3][2] = 42.0;
}

int main(int argc,char *argv[])
{
    int rows = atol(argv[1]);
    int cols = atol(argv[2]);

    //TO DO
    //Allocate memory for array here
    fun1();
    printf("Array[3][2]=%f\n",array[3][2]);

    return(0);
}

Solution

  • Unfortunately, it not quite possible in C to achieve what you are asking for.

    There is a slightly ugly solution with a macro. Since the macro refers to both the global array itself and a global variable containing its second dimension, you have to be careful to not shadow the globals in any function which uses the macro. Here, I used clunky names ending with underscores to try to avoid name reuse.

    Other than that, it should work:

    void*  global_array_void_;
    size_t global_array_minor_dim_;
    
    #define global_array ((double(*)[global_array_minor_dim_])global_array_void_)    
    

    Before you can use the macro, you need to allocate it and initialize the global variables:

    void init_global_array(int rows, int cols) {
        global_array_minor_dim_ = cols
        global_array_void_ = malloc(rows * cols * sizeof global_array[0][0]);
    }
    

    From then on, you can use use global_array as though it were a regular array:

    void f(int r, int c, double v) {
        global_array[r][c] = v;
    }
    

    Live on coliru.

    The type in the cast ((double (*)[cols])(array_void_)) might not be obvious. It represents a pointer to an array of cols doubles, which is what double[][cols] would decay to as a pointer. Note that double[][cols] does not decay to double**, which is a completely different (and incompatible) type.

    With that definition, sizeof global_array[r] has the correct value: cols * sizeof(double). Contrast that with sizeof argv[i].


    A more traditional way of doing this is to use a function to compute the precise index. That still depends on the minor dimension being available:

    double* global_array;
    size_t global_array_minor_dim_;
    
    void init_global_array(int rows, int cols) {
        global_array_minor_dim_ = cols
        global_array_void_ = malloc(rows * cols * sizeof global_array[0][0]);
    }
    
    double* global_array_at(int r, int c) {
        return &global_array[r * global_array_minor_dim_ + c];
    }
    

    Now you can use *global_array_at(r, c) as a replacement for global_array[r][c]. In C, it's impossible to eliminate the * and still have assignment work (in C++, the function could have returned a double& instead of a double*), but this could be solved, once again, with a macro.