Search code examples
cpointersmallocc99c89

Rewriting multi-dimensional VLAs in C89


I'm rewriting a bunch of C99 VLAs for an open source project for support in Visual Studio. So I have a lot of statements like

void somefunc(double var[r]){...}

which I rewrite to

void somefunc(double *var) {
     var = malloc(sizeof(double) * r);
     ...
     free(var);
}

Which I am assuming to be the correct way to do this.
The problem is I am starting to see some more complicated VLA declarations which are leaving me stumped. For example:

double *(*var)[length1][length2][length3] = malloc(sizeof(double *[length4][length1][length2][length3]));

So to me this essentially looks like a 5 dimensional array. This is clearly illegal in C89 because of the use of variables in the sizeof() function.

My guess on how to rewrite this would be:

double *****var = malloc(sizeof(double ****) * length1);
      for(int i = 0; i<length1; i++) {
            var[i] = malloc(sizeof(double ***) * length2);
            for(int j = 0; j<length2; j++) {
                 var[i][j] = malloc(sizeof(double **) * length3);
                      for(int k = 0; k<length3; k++) {
                           var[i][j][k] = malloc(sizeof(double *) * length4);
                      } 
            }
      }

As messy as it is, this is my best guess on how to rewrite the above statement. Is this technically correct, and is there a better way I should be going about this?


Solution

  • Here's my attempt:

    double***** var = malloc(sizeof *var * length4);
    
    for (int i = 0; i < length4; ++i)
    {
        var[i] = malloc(sizeof *var[i] * length1);
    
        for (int j = 0; j < length1; ++j)
        {
            var[i][j] = malloc(sizeof *var[i][j] * length2);
    
            for (int k = 0; k < length2; ++k)
            {
                var[i][j][k] = malloc(sizeof *var[i][j][k] * length3);
    
                for (int l = 0; l < length3; ++l)
                {
                    var[i][j][k][l] = NULL;     
                    // var[i][j][k][l] is a double* - you can allocate some room for a double here, or assign the address of an existing variable
                }
            }
        }
    }
    

    Similar to yours, except that note how I used length4 in the first malloc. If you look at your original declaration, var is a pointer to a three dimensional array of double*. As we can deduce from the malloc call on the right, memory for length4 such arrays is allocated, so you can think of var as an array of length4 three-dimensional arrays of double*.

    I still maintain that anyone who puts such stuff in production code ought to be shot on the spot (well, you, as a maintainer, are excused).

    IIRC declaring variables inside fors isn't valid C89, but you can move these above to the top of your scope.

    Note that, as @immibis points out in the comments, your first conversion is most likely a mistake; double *var is an argument to the function, which means it's passed by value, so any changes you make to it inside the function aren't visible outside, and on top of that, you malloc some memory, do stuff with it, then free it. Even if you were modifying a passed pointer - which would require the parameter type to be double** - it still isn't necessary to pass a variable so you can use it locally exclusively. You most likely wanted to malloc the memory outside of the function and pass a valid pointer (and hopefully the size) to it.