Matrice di dimensioni variabili in C.
Domanda
Esiste un modo per creare un array di dimensioni doppie con script doppio in C (non C ++, solo C)? So che per creare un array di dimensioni singole con script singolo, basta usare un puntatore, ad esempio
float *array;
array = (float *) calloc(sizeof(float), n);
crea un array singolarmente con script di float di dimensione n. C'è qualcosa di simile che posso fare per gli array doppiamente scritti?
Soluzione
Non ci sono matrici con doppio script in C; ci sono solo matrici di matrici. Per esempio. in questo modo:
int a[3][3];
Dovrebbe essere letto come "array di 3 array di 3 pollici", non come "array di 3x3 pollici". Ciò è immediatamente visibile dai tipi di espressioni - ad es. a [0]
è un'espressione valida e il suo tipo è int [3]
.
Per i tipi di array, la dimensione dell'array fa parte del tipo e pertanto deve essere nota al momento della compilazione. Pertanto, mentre puoi avere un tipo " puntatore agli array " per rendere dinamica una dimensione, le restanti dovrebbero ancora essere riparate:
int (*p)[3] // pointer to arrays of 3 ints each
Esistono due soluzioni alternative tradizionali:
-
Usa semplicemente un array dinamico monodimensionale di elementi larghezza x altezza e calcola gli indici 1D da coordinate 2D come
(y * larghezza + x)
te stesso -
Usa il puntatore ai puntatori:
int** a = malloc(sizeof(int*) * height); for (i = 0; i < height; ++i) a[i] = malloc(sizeof(int) * width); a[0][0] = 123; ...
Il problema qui è che il tuo array non deve più essere rettangolare e non puoi davvero applicarlo. Per quanto riguarda le prestazioni, è anche peggio di un singolo blocco contiguo di memoria.
In C99, puoi anche usare matrici di lunghezza variabile:
void foo(int width, int height) {
int a[width][height];
...
}
Altri suggerimenti
Le comp.lang.c FAQ ha una buona sezione in merito.
Puoi fare quasi la stessa cosa per array multidimensionali.
float **array;
array = calloc(sizeof(float*), n);
for(int i = 0; i < n; i++)
{
array[i] = calloc(sizeof(float), n);
}
Se vuoi una matrice con n righe e m colonne, puoi usare un array lineare di lunghezza m * n
per rappresentare questo, dove ogni indice i
rappresenta
row = i / n
col = i % n
e la mappatura inversa
i = row * n + col
La maggior parte dei pacchetti di algebra che usano matrici come matlab in realtà usano questa rappresentazione, perché si generalizza bene a qualsiasi dimensione (si potrebbe generalizzare anche questa a una matrice tridimensionale).
No, non è possibile. In alternativa, allocare un singolo array e definire una funzione di indicizzazione che prende le coordinate e restituisce un indice nell'array.
int Index(int i, int j, int numCols)
{
return i * numCols + j;
}
int numRows = 100;
int numCols = 200;
float *data = malloc(sizeof(float) * numRows * numCols);
data[Index(34, 56, numCols)] = 42.0f;
Puoi usare array di lunghezza variabile C99 (funziona con gcc):
#include <stdio.h>
#include <stdlib.h>
void foo(size_t rows, size_t cols, float array[rows][cols])
{
printf("%f\n", array[2][3]);
}
int main(void)
{
size_t rows = 4;
size_t cols = 5;
float (*array)[cols] = calloc(sizeof (float), rows * cols);
array[2][3] = 42;
foo(rows, cols, array);
}
Sono sorpreso che nessuno abbia sottolineato l'alternativa "ovvia" che preserva la singola allocazione contigua per la matrice principale, ma ha il vettore di puntatori per fornire un doppio abbonamento. (Suppongo che ciò significhi che non è ovvio, dopo tutto.)
float **array2d = malloc(sizeof(*array2d) * height);
float *array1d = malloc(sizeof(*array1d) * height * width);
for (i = 0; i < height; ++i)
array2d[i] = &array1d[i * width];
Ora puoi scrivere accessi in array 2D come al solito:
array2d[0][0] = 123.0;
Chiaramente, dobbiamo anche controllare l'allocazione della memoria.