55

In C can I pass a multidimensional array to a function as a single argument when I don't know what the dimensions of the array are going to be?

Besides, my multidimensional array may contain types other than strings.

Arsen Khachaturyan
  • 7,904
  • 4
  • 42
  • 42
David
  • 14,047
  • 24
  • 80
  • 101

5 Answers5

36

Pass an explicit pointer to the first element with the array dimensions as separate parameters. For example, to handle arbitrarily sized 2-d arrays of int:

void func_2d(int *p, size_t M, size_t N)
{
  size_t i, j;
  ...
  p[i*N+j] = ...;
}

which would be called as

...
int arr1[10][20];
int arr2[5][80];
...
func_2d(&arr1[0][0], 10, 20);
func_2d(&arr2[0][0], 5, 80);

Same principle applies for higher-dimension arrays:

func_3d(int *p, size_t X, size_t Y, size_t Z)
{
  size_t i, j, k;
  ...
  p[i*Y*Z+j*Z+k] = ...;
  ...
}
...
arr2[10][20][30];
...
func_3d(&arr[0][0][0], 10, 20, 30);
John Bode
  • 119,563
  • 19
  • 122
  • 198
  • 2
    `p[i*Y+j*Z+k]` should be `p[i*Y*Z+j*Z+k]` instead. – David H Aug 18 '12 at 09:01
  • http://stackoverflow.com/questions/16943909/manipulate-multidimensional-array-in-a-function – Dchris Jun 05 '13 at 16:02
  • whats the values of i and j? – AlphaGoku Apr 06 '16 at 09:17
  • Thank you very much for your answer, @John Bode. Could you please explain why are you doing `p[i*N+j]` to address a specific element of the multidimensional array ? – F. Zer Oct 12 '21 at 16:52
  • @F.Zer - Because we're treating `p` as a 1-D array. For example, if we pass the address of the first element of a 2x2 array, then we treat `p` as though it were a single-dimensioned array of 4 elements. `p[0] == a[0][0]`, `p[1] == a[0][1]`, `p[2] == a[1][0]`, and `p[3] == a[1][1]`. So to access `a[i][j]` through `p`, we need to use `p[ i * 2 + j ]`. – John Bode Oct 12 '21 at 17:12
  • Thank you so much, @John Bode ! I could understand ! Just to be sure, the 2 in `p[ i * 2 + j ]` refers to the number of _columns_, right ? – F. Zer Oct 12 '21 at 19:23
  • 1
    @F.Zer - Yes. In this example, we have two columns per row. – John Bode Oct 12 '21 at 19:56
24

You can declare your function as:

f(int size, int data[][size]) {...}

The compiler will then do all pointer arithmetic for you.

Note that the dimensions sizes must appear before the array itself.

GNU C allows for argument declaration forwarding (in case you really need to pass dimensions after the array):

f(int size; int data[][size], int size) {...}

The first dimension, although you can pass as argument too, is useless for the C compiler (even for sizeof operator, when applied over array passed as argument will always treat is as a pointer to first element).

rslemos
  • 2,454
  • 22
  • 32
  • 2
    IMO this should be the accepted answer. No extra code needed and no unnecessary heap allocations. simple and clean – kjh Apr 27 '15 at 17:03
  • Thanks @kjh, I also think this is the cleanest solution. Accepted answer is the one that worked for him. Look: the OP is from 2008, almost 6 years before my answer. Besides that, I don't know if back then C standards allowed for the syntax I've used here. – rslemos Apr 30 '15 at 03:23
  • This is the solution I have finally adopted for passing an integer matrix (a two-dimentional array) of size M x N as a function argument. Maybe a little more information will be helpful: The function prototype is like: void f(int N, int data[][N], int M); In the body of the function, element [m][n] can be written as data[m][n] - very convenient, no index computation is needed. – jonathanzh May 19 '15 at 09:08
  • I declare function as you said, I call it from main() and it's ok, but how should I declare the variable `data` in my main() if I don't know size(s)? I tried with `int* data` but won't work. – glc78 Aug 27 '17 at 11:30
  • @glc78 Either as a VLA on the stack `int data[height][width];` or on the heap with `int (*data)[width] = malloc(height*sizeof(*data));`. In both cases, you can subsequently access `data[y][x]` in the normal way and pass it to `f(width, data)`. – cmaster - reinstate monica Dec 13 '19 at 09:13
23

You can do this with any data type. Simply make it a pointer-to-pointer:

typedef struct {
  int myint;
  char* mystring;
} data;

data** array;

But don't forget you still have to malloc the variable, and it does get a bit complex:

//initialize
int x,y,w,h;
w = 10; //width of array
h = 20; //height of array

//malloc the 'y' dimension
array = malloc(sizeof(data*) * h);

//iterate over 'y' dimension
for(y=0;y<h;y++){
  //malloc the 'x' dimension
  array[y] = malloc(sizeof(data) * w);

  //iterate over the 'x' dimension
  for(x=0;x<w;x++){
    //malloc the string in the data structure
    array[y][x].mystring = malloc(50); //50 chars

    //initialize
    array[y][x].myint = 6;
    strcpy(array[y][x].mystring, "w00t");
  }
}

The code to deallocate the structure looks similar - don't forget to call free() on everything you malloced! (Also, in robust applications you should check the return of malloc().)

Now let's say you want to pass this to a function. You can still use the double pointer, because you probably want to do manipulations on the data structure, not the pointer to pointers of data structures:

int whatsMyInt(data** arrayPtr, int x, int y){
  return arrayPtr[y][x].myint;
}

Call this function with:

printf("My int is %d.\n", whatsMyInt(array, 2, 4));

Output:

My int is 6.
M.M
  • 138,810
  • 21
  • 208
  • 365
andrewrk
  • 30,272
  • 27
  • 92
  • 113
  • help needed here:http://stackoverflow.com/questions/16943909/manipulate-multidimensional-array-in-a-function – Dchris Jun 05 '13 at 16:01
  • 6
    A pointer to pointer segmented lookup table is not a 2D array. Just because it allows `[][]` syntax, it doesn't magically turn into an array. You can't memcpy() it etc because the memory is not allocated in adjacent memory cells, which is required for arrays. Your lookup table is rather scattered all over the heap, making lookups slow and the heap fragmented. – Lundin Jun 23 '15 at 10:42
1

In C can I pass a multidimensional array to a function as a single argument when I don't know what the dimensions of the array are going to be?

No

If by "single argument" you mean passing just the array without passing the array dimensions, no you can't. At least not for true multidimensional arrays.

You can put the dimension[s] into a structure along with the array and claim you're passing a "single argument", but that's really just packing multiple values into a single container and calling that container "one argument".

You can pass an array of known type and number of dimensions but unknown size by passing the dimensions themselves and the array like this:

void print2dIntArray( size_t x, size_t y, int array[ x ][ y ] )
{
    for ( size_t ii = 0, ii < x; ii++ )
    {
        char *sep = "";
        for ( size_t jj = 0; jj < y; jj++ )
        {
            printf( "%s%d", sep, array[ ii ][ jj ] );
            sep = ", ";
        }
        printf( "\n" );
    }
}

You would call that function like this:

int a[ 4 ][ 5 ];
int b[ 255 ][ 16 ];

...

print2dIntArray( 4, 5, a );

....

printt2dIntArray( 255, 16, b );

Similarly, a 3-dimensional array of, for example, a struct pixel:

void print3dPixelArray( size_t x, size_t y, size_t z, struct pixel pixelArray[ x ][ y ][ z ] )
{
    ...
}

or a 1-dimensional double array:

void print1dDoubleArray( size_t x, double doubleArray[ x ] )
{
    ...
}

BUT...

However, it can be possible to pass "arrays of pointers to arrays of pointers to ... an array of type X" constructs that are often mislabeled as a "multidimensional array" as a single argument as long as the base type X has an sentinel value that can be used to indicate the end of the final, lowest-level single-dimensional array of type X.

For example, the char **argv value passed to main() is a pointer to an array of pointers to char. The initial array of char * pointers ends with a NULL sentinel value, while each char array referenced by the array of char * pointers ends with a NUL character value of '\0'.

For example, if you can use NAN as a sentinel value because actual data won't ever be a NAN, you could print a double ** like this:

void printDoubles( double **notAnArray )
{
    while ( *notAnArray )
    {
        char *sep = "";
        for ( size_t ii = 0;  ( *notAnArray )[ ii ] != NAN; ii++ )
        {
            printf( "%s%f", sep, ( *notAnArray )[ ii ] );
            sep = ", ";
        }

        notAnArray++;
    }
}
Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
-2
int matmax(int **p, int dim) // p- matrix , dim- dimension of the matrix 
{
    return p[0][0];  
}

int main()
{
   int *u[5]; // will be a 5x5 matrix

   for(int i = 0; i < 5; i++)
       u[i] = new int[5];

   u[0][0] = 1; // initialize u[0][0] - not mandatory

   // put data in u[][]

   printf("%d", matmax(u, 0)); //call to function
   getche(); // just to see the result
}
Markus Safar
  • 6,324
  • 5
  • 28
  • 44
Pedro
  • 15
  • 1