Passing a multidimensional variable length array to a function

38,813

Solution 1

Passing arrays to functions is a bit funny in C and C++. There are no rvalues of array types, so you're actually passing a pointer.

To address a 2D array (a real one, not array of arrays), you'll need to pass 2 chunks of data:

  • the pointer to where it starts
  • how wide one row is

And these are two separate values, be it C or C++ or with VLA or without or whatnot.

Some ways to write that:

Simplest, works everywhere but needs more manual work

void foo(int width, int* arr) {
    arr[x + y*width] = 5;
}

VLA, standard C99

void foo(int width, int arr[][width]) {
    arr[x][y] = 5;
}

VLA w/ reversed arguments, forward parameter declaration (GNU C extension)

void foo(int width; int arr[][width], int width) {
    arr[x][y]=5;
}

C++ w/ VLA (GNU C++ extension, terribly ugly)

void foo(int width, int* ptr) {
    typedef int arrtype[][width];
    arrtype& arr = *reinterpret_cast<arrtype*>(ptr);
    arr[x][y]=5;
}

Big remark:

The [x][y] notation with a 2D array works because the array's type contains the width. No VLA = array types must be fixed at compile-time.

Hence: If you can't use VLA, then...

  • there's no way to handle it in C,
  • there's no way to handle it without a proxy class w/ overloaded operator overloading in C++.

If you can use VLA (C99 or GNU C++ extensions), then...

  • you're in the green in C,
  • you still need a mess in C++, use classes instead.

For C++, boost::multi_array is a solid choice.

A workaround

For 2D arrays, you can make two separate allocations:

  • a 1D array of pointers to T (A)
  • a 2D array of T (B)

Then set the pointers in (A) to point into respective rows of (B).

With this setup, you can just pass (A) around as a simple T** and it will behave well with [x][y] indexing.

This solution is nice for 2D, but needs more and more boilerplate for higher dimensions. It's also slower than the VLA solution because of the extra layer of indirection.

You may also run into a similar solution with a separate allocation for every B's row. In C this looks like a malloc-in-a-loop, and is analogous of C++'s vector-of-vectors. However this takes away the benefit of having the whole array in one block.

Solution 2

There is no clear cut way for doing this but you can use a workaround to treat a 2 dimensional array as a one dimensional array and then reconvert it to a two dimensional array inside the function.

void foo2(int n, int *arr) 
{
    int *ptr; // use this as a marker to go to next block
    int i;
    int j;

    for(i = 0; i < n; i++)
    {
        ptr = arr + i*n; // this is the starting for arr[i] ...
        for (j = 0; j < n ;j++)
        {
            printf(" %d ", ptr[j]); // This is same as arr[i][j]
        }
    }
}

void bar2()
{
    int arr[10][10];
    foo2(10, (int *)arr);
}
Share:
38,813
alveko
Author by

alveko

Updated on July 09, 2022

Comments

  • alveko
    alveko almost 2 years

    There are tons of similar questions, but still I could not find any answer relevant for the feature of variable length arrays in C99/C11.

    How to pass multidimensional variable length array to a function in C99/C11?

    For example:

    void foo(int n, int arr[][]) // <-- error here, how to fix?
    {
    }
    
    void bar(int n)
    {
        int arr[n][n];
        foo(n, arr);
    }
    

    Compiler (g++-4.7 -std=gnu++11) says:
    error: declaration of ‘arr’ as multidimensional array must have bounds for all dimensions except the first

    If I change it to int *arr[], compiler still complains:
    error: cannot convert ‘int (*)[(((sizetype)(((ssizetype)n) + -1)) + 1)]’ to ‘int**’ for argument ‘2’ to ‘void foo(int, int**)’

    Next question, how to pass it by value and how to pass it by reference? Apparently, usually you don't want the entire array to be copied when you pass it to a function.

    With constant length arrays it's simple, since, as the "constant" implies, you should know the length when you declare the function:

    void foo2(int n, int arr[][10]) // <-- ok
    {
    }
    
    void bar2()
    {
        int arr[10][10];
        foo2(10, arr);
    }
    

    I know, passing arrays to functions like this is not a best practice, and I don't like it at all. It is probably better to do with flat pointers, or objects (like std:vector) or somehow else. But still, I'm a bit curios what is the answer here from a theoretical standpoint.

  • a3f
    a3f about 8 years
    Yet another option: void foo(arr, width) int width; int arr[][width] {}. reversed args w/ K&R syntax. I haven't seen it in the wild yet though.
  • rampion
    rampion over 7 years
    in C++11 (with Clang, at least), you can prettify the C++ example to auto arr = reinterpret_cast<int (&)[][width]>(ptr);
  • rampion
    rampion over 7 years
    Or if you want an explicit type, you may find using arrtype = int[][width]; prettier than typedef int arrtype[][width];
  • Tomáš Zato
    Tomáš Zato over 7 years
    How do I call each function? C variable length array isn't int* type.
  • Valdrinium
    Valdrinium over 5 years
    This is very educative
  • young_souvlaki
    young_souvlaki over 2 years
    VLA - Variable Length Array.