Aggiungo solo una coppia di brevi esempi (ormai una FAQ) sull'allocazione dinamica "da manuale" di array multidimensionali.
Esempio di base, volutamente incompleto:
codice:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
size_t righe, colonne, i, j;
int **matrice;
printf(" Righe........: ");
scanf("%d", &righe);
printf(" Colonne......: ");
scanf("%d", &colonne);
puts("");
matrice = (int **)malloc(righe * sizeof(int *));
if (NULL == matrice)
{
fputs("Errore di allocazione base array !\n", stderr);
return 1;
}
for (i = 0; i < righe; ++i)
{
int *p;
p = (int *)malloc(colonne * sizeof(int));
if (NULL != p)
{
matrice[i] = p;
}
else
{
fprintf(stderr, "Errore di allocazione alla riga %d!\n", i);
while (i--)
{
free(matrice[i]);
}
free(matrice);
return 2;
}
}
for(i = 0; i < righe; ++i)
{
for(j = 0; j < colonne; ++j)
{
printf("Inserisci elemento riga %d e colonna %d: ", i, j);
scanf("%d", &matrice[i][j]);
puts("");
}
}
for(i = 0; i < righe; ++i)
{
printf("matrice[%d] = {%d", i, matrice[i][0]);
for(j = 1; j < colonne; ++j)
{
printf(", %d", matrice[i][j]);
}
puts("}");
free(matrice[i]);
}
free(matrice);
return 0;
}
Esempio di allocazione indiretta, entro una funzione accessoria.
codice:
/************************************************************************/
/** Scopo del programma:...............................................**/
/************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define ASIZE (8)
typedef enum {FALSE, TRUE} Bool_t;
Bool_t matrix_alloc(int ***m, const size_t row, const size_t col)
{
size_t i;
assert(row > 0);
assert(col > 0);
/*
** PRIMO STEP: allocazione array di base
*/
*m = (int **)malloc(row * sizeof(int *));
if (NULL == *m)
{
fputs("Errore di allocazione base array !\n", stderr);
return (FALSE);
}
for (i = 0; i < row; ++i)
{
int *p;
/*
** SECONDO STEP: allocazione di ogni singolo array-riga
*/
p = (int *)malloc(col *sizeof(int));
if (NULL != p)
{
/*
** TERZO e ULTIMO STEP: collegamento riga ad array base
*/
(*m)[i] = p;
}
else
{
fprintf(stderr, "Errore di allocazione alla riga %d!\n", i);
while (i--)
{
free((*m)[i]);
fprintf(stderr, "%d ", i);
}
free(*m);
return (FALSE);
}
}
return (TRUE);
}
int main(void)
{
int **array;
size_t righe, colonne;
size_t i, j;
Bool_t status;
righe = ASIZE;
colonne = ASIZE;
printf("Allocazione dinamica di un array "
"bidimensionale %d x %d\n",
righe, colonne);
status = matrix_alloc(&array, righe, colonne);
if(status)
{
printf("** Allocazione effettuata. "
"Inizializzazione di %d celle in corso...\n",
righe * colonne);
for (i = 0; i < righe; ++i)
{
for (j = 0; j < colonne; ++j)
{
array[ i ][ j ] = i * 10 + j;
}
}
puts("** Contenuto dell'array:");
for (i = 0; i < righe; ++i)
{
for (j = 0; j < colonne; ++j)
{
printf( "array[%d][%d] = %02d\n", i, j, array[ i ][ j ]);
}
free(array[i]);
}
free(array);
}
return (EXIT_SUCCESS);
}/* EOF: alloc_array.c */
Infine, un esempio ancora più avanzato che mostra una panoramica di metodi di allocazione dinamica a vari livelli di indirezione.
Aggiungo solo un paio di note che forse potranno tornare utili all'OP solo in una fase più avanzata dello studio.
1) L'uso di jagged arrays va imparato (per scopi scolastici) e poi dimenticato. Nel real world l'allocazione in un blocco unico impera nel 99% del codice, per motivi che spaziano dalle prestazioni, alla robustezza, alla manutenibilità del codice: i rari casi nei quali si ricorre ad un array di arrays come nella manualistica e nella didattica sono legati, ad esempio, alla rappresentazione di matrici sparse e/o di array di liste, alberi ed altri ADT che possano beneficiare della potenzialità di una simile struttura, ossia la capacità di referenziare con un solo array di base altri array di lunghezze potenzialmente molto diverse tra loro.
2) In linguaggio C++ è comunque del tutto sconsigliato l'uso degli array C-alike. Esistono apposite classi come vector, valarray, list, string e altre. Si veda questo famoso thread per una spiegazione in merito.