Veo que conoces malloc así que no hay problema en que uses memoria dinámica.
A la hora de generar el array házlo de forma dinámica.
El prototipo de la función que recibiera tu array sería éste:
void muestra(int **array, int filas, int columnas);
Esto es así porqué si le pasas un array estático estás obligado a darle todas las dimensiones menos la de mayor peso. Esto es así porque C necesita conocer el tamaño de los objetos que va a usar, y cómo se escapa de la explicación de esta respuesta te lo tendrás que creer.
Ahora llega la hora de crear el array. Suponiendo que el número de filas ya está en
n_filas y el número de columnas ya está en
n_columnas, lo dimensionas de esta forma:
int **mi_array = malloc(n_filas * sizeof(int*));
for(int i = 0; i < n_filas; ++i)
mi_array[i] = malloc(n_columnas * sizeof(int));
A la hora de darle un dato a una posición cualquiera del array suponiendo
m el número de fila,
n el número de columna y
x el dato a guardar:
Lo mismo que para mostrarlo:
printf("%d", mi_array[m][n]);
Y a la hora de pasar el array a la función:
muestra(mi_array, n_filas, n_columnas);
Ahora pensarás:
Pero si C necesita saber el tamaño de un objeto para poderlo usar ¿Por qué se le pasa mi_array sin dimensión alguna, en vez de hacer mi_array[][20], por ejemplo?
La respuesta es que todos los punteros tienen el mismo tamaño. Todos.
Así que te aseguras, de esta forma, que puedes pasarle a esta función un array con un tamaño arbitrario de sus dimensiones que te va a funcionar siempre que los valores que le pases a
filas y
columnas sean los que realmente tiene dicho array.
Muy importante, eso sí, cuándo dejes de utilizar el array debes liberarlo y eso se debe hacer desde dentro a afuera. Así:
for(int i = 0; i < n_columnas; ++i)
free(mi_array[i]);
free(mi_array);