do language features invoke a preference?
Assolutamente sì. A causa del modo in cui C tratta le espressioni dell'array, passare un array già assegnato (statico, automatico o dinamico) come parametro a una funzione è lontano più preferibile all'array allocare spazio e restituire un puntatore al chiamante.
In primo luogo, le basi:
Tranne quando è l'operando degli operatori sizeof
o unary &
, o è una stringa letterale usata per inizializzare un'altra matrice in una dichiarazione, un'espressione di tipo "N-elemento array of T
"sarà convertito (" decay ") in un'espressione di tipo" puntatore a T
", e il valore dell'espressione sarà l'indirizzo del primo elemento dell'array 1 . IOW, le espressioni degli array perdono il loro "array-ness" nella maggior parte dei casi.
Quando si chiama una funzione con un parametro dell'array, come
int arr[N];
...
foo( arr );
l'espressione arr
nella chiamata a foo "decadera" dal tipo int [N]
a int *
e cosa foo
riceve sarà un valore puntatore, non un oggetto matrice :
void foo( int *arr ) // T a[N] and T a[] are treated as T *a
{ // in a function parameter declaration
...
}
Non è possibile restituire un oggetto oggetto di una matrice - C non consente alle funzioni di restituire i tipi di array (un prototipo come int foo()[10]
non è consentito). A causa della regola di conversione di cui sopra, return arr;
termina restituendo un puntatore al primo elemento:
int *foo( void )
{
int arr[N];
...
return arr; // equivalent to return &arr[0]
}
Eccetto arr
cessa di esistere una volta che foo
esce 2 , in modo che il valore del puntatore restituito sia non valido , e tentando di dereferenziare porterà a un comportamento indefinito.
A questo punto, hai due scelte: rendere la funzione responsabile per l'allocazione dinamica del buffer di destinazione e il rinvio del risultato al chiamante, oppure rendere il chiamante responsabile per allocare il buffer di destinazione (dinamicamente o meno) e passarlo a la funzione.
Ricorda che lo standard C non definisce alcun tipo di garbage collection automatico per la memoria dinamica: tutta la memoria dinamica deve essere esplicitamente deallocata dalle chiamate a free
.
La prima opzione divide i compiti di gestione della memoria tra la funzione e il chiamante, il che non è desiderabile. Chiunque usi la funzione deve essere consapevole che la funzione sta allocando dinamicamente memoria e che è responsabile di liberare quella memoria quando ha finito con essa. Se l'utente della funzione non presta attenzione alla documentazione e non libera la memoria restituita dalla funzione, si verifica una perdita di memoria.
La seconda opzione mette tutta la responsabilità della gestione della memoria su una parte. La funzione chiamata ottiene un puntatore e la dimensione massima del buffer - non deve preoccuparsi se la memoria puntata è staticamente, automaticamente o allocata dinamicamente. Questo metodo è più sicuro e più robusto, motivo per cui lo si vede più spesso nel codice C.
In Java, gli array sono trattati allo stesso modo di quasi tutti gli altri tipi di dati (non "decadono" in un tipo di puntatore), quindi è più naturale che la funzione restituisca semplicemente una matrice al chiamante. Anche se gli array vengono allocati dall'heap quando vengono creati, non devi preoccuparti di disassegnarli quando hai finito: verranno automaticamente raccolti tramite garbage quando il loro conteggio dei riferimenti va a 0.
- "Un array è solo un puntatore" è un'errata affermazione di questa regola. Le matrici non sono . Le espressioni della matrice vengono convertite nelle espressioni del puntatore nella maggior parte delle circostanze, ma un oggetto non è un puntatore.
- Se dichiariamo
arr
con la parola chiave static
, verrà mantenuta dopo la chiusura della funzione, ma il codice non sarà più rientrante.