[variable length arrays](https://en.wikipedia.org/wiki/Variable-length_array) are required in C99 and optional in C11, C17/18.

To detect, something like

    #if defined(__STDC__) && defined(__STDC_VERSION__) && \
        (__STDC_VERSION__ == 199901 || (__STDC_VERSION__ >= 201112 && __STDC_NO_VLA__ != 1))
        #define VLA_OK 1
    #else
        #define VLA_OK 0
    #ednif



If code does not use a variable length arrays, code could take advantage that the address of the first element of the 2D array is _equivalent_ to  `&arr[0][0]` and that 2D arrays are continuous.

    void fill(void *arr, size_t rows, size_t cols, int val) {
       int *a = arr;
       for(size_t i = 0; i < rows; i++) {
         for(size_t j = 0; j < cols; j++) {
           *a++ = val;
        }
      }
    }

Sample usage

    #define IN 5
    #define JN 3
    int arr[IN][JN];
    fill(arr, IN, JN, 0x45);

This does lose type checking.  
Code instead could oblige passing the address of the first `int`

    //        v---v---- type checked
    void fill(int *arr, size_t rows, size_t cols, int val) {
       for(size_t i = 0; i < rows; i++) {
         for(size_t j = 0; j < cols; j++) {
           *arr++ = val;
        }
      }
    }

Sample usage

    #define IN 5
    #define JN 3
    int arr[IN][JN];
    fill(&arr[0][0], IN, JN, 0x45);
    // or
    fill(arr[0], IN, JN, 0x45);