Dynamically allocating a 2D array can be done in several ways.
Assuming you have a C99 compiler or C2011 compiler that supports variable-length arrays, this is dead easy:
int rows, columns;
...
scanf("%d %d",&rows,&columns);
int array[rows][columns];
array
is a variable-length array (VLA), meaning its dimensions are established at run time. To pass this to another function, the prototype would simply be
int *max5( int rows, int columns, int arr[rows][columns] ) { ... }
which can also be written as
int *max5( int rows, int columns, int (*arr)[columns] ) { ... }
I'll get into that second form below. Note that both rows
and columns
must be declared before you use them in the VLA declaraion.
You would call it as
int *maxarr = max5( rows, columns, array );
The advantage of this approach is that a) it's easy, and b) the memory for array
is allocated contiguously (i.e., as a single, continuous block of memory) and released as soon as you exit it's enclosing scope; there's no need to manually free
it. The disadvantage of this approach is that rows
and columns
can't be arbitrarily large, you can't initialize the array using regular initializer syntax (i.e., you can't write int array[rows][columns] = { ... };
), and you can't define VLAs at file scope (i.e., as a global variable). There's also the problem that VLA support has always been a little spotty, and the 2011 standard now makes them optional, so it's not guaranteed they'll be supported everywhere.
If rows
or columns
is very large, or if for some other reason you want to allocate this memory from the heap, it's just slightly more complicated:
int rows, columns;
...
scanf("%d %d", rows, columns);
int (*array)[columns] = malloc( sizeof *array * rows );
In this version, array
is a pointer to an N-element array of int
(where N == columns
), and we malloc
space to hold M such arrays (where M == rows
). Again, this takes advantage of VLA syntax, but instead of allocating the memory from the stack frame, we allocate it from the heap. Like in the first method, the memory is allocated contiguously. You would index into it as you would any other array:
array[i][j] = x;
When you're done with the array, you would free it as
free( array );
The prototype for max5
would be
int *max5( int rows, int columns, int (*arr)[columns] ) { ... }
Look familiar? That's the second version we used earlier when we were passing a 2D VLA.
Except when it is the operand of the sizeof
or unary &
operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T
" will be converted to an expression of type "pointer to T
", and the value of the expression will be the address of the first element in the array.
In the first case where we declared array
as a 2D VLA, when you call
int *maxarr = max5( rows, columns, array );
the expression array
is converted from type "rows
-element array of columns
-element array of int
" to "pointer to columns
-element array of int
", so what max5
receives is a pointer value, not an array. It just so happens that in the context of a function parameter declaration, T a[N]
and T a[]
are both interpreted as T *a
; IOW, all three declare a
as a pointer to T
.
Thus, both
int *max5( int rows, int columns, int arr[rows][columns] ) { ... }
and
int *max5( int rows, int columns, int (*arr)[columns] ) { ... }
declare arr
as a pointer to an array, not a 2D array. Similarly, both return a simple int *
, as opposed to an array of int
. The reason for this is simple; if you return a subarray using
return arr[i];
the expression arr[i]
will have type "columns
-element array of int
"; by the rule above, that expression will be converted to type int *
. C doesn't allow you to return array objects as arrays, nor can an array expression be the target of an assignment.
Now, what if you're working with a C89 compiler, or a C2011 compiler that doesn't support VLAs? In this case, you'd need to allocate the memory in a piecemeal fashion, like so:
int rows, columns;
int **array;
...
scanf("%d %d", rows, columns);
array = malloc( sizeof *array * rows );
if ( array )
{
int i;
for ( i = 0; i < rows; i++ )
{
array[i] = malloc( sizeof *array[i] * columns );
}
}
Your max5
prototype would now be
int *max5( int rows, int columns, int **arr );
You're not dealing with arrays or pointers to arrays anymore; you're dealing with a double pointer, which is not the same thing as a pointer to a 2D array. You can still subscript it as though it were a 2D array
arr[i][j] = x;
but it is not an array object in and of itself.
The most important thing to realize is that the memory is not guaranteed to be contiguous; rows may not be adjacent in memory, so you don't want to try to walk down rows using a pointer, like so:
int (*p)[columns] = arr;
while ( some_condition )
p++; // sets p to point to the next row
For an array that was allocated piecemeal, that sort of algorithm won't work.
Also, since the memory was allocated piecemeal, it needs to be freed piecemeal:
for ( i = 0; i < rows; i++ )
free(array[i]);
free( array );
Hope that helps.