Questa decisione di progettazione deve essere inserita nel contesto storico. C è stato creato per implementare UNIX su un PDP11 , una CPU a 16 bit con meno potenza di calcolo rispetto a qualsiasi smartphone di oggi, e max 4 MB di memoria. L'ottimizzazione dei compilatori era molto meno avanzata di oggi. L'implementazione di un sistema operativo su tali macchine ha richiesto di evitare sovraccarichi non necessari e, poiché questo era lo scopo principale del linguaggio, non è sorprendente che le funzionalità linguistiche favoriscano le prestazioni.
Il passaggio di una matrice di dimensioni note per valore richiederebbe di spostare tutto il contenuto dell'array nello stack. Questa funzione incoraggerebbe il consumo di memoria, quando la memoria era scarsa e lo spostamento della memoria era lento. Passare un array di dimensioni variabili avrebbe richiesto di spingere la dimensione e il contenuto e calcolare dinamicamente dove trovare gli argomenti rimanenti nello stack. Un sovraccarico inaccettabile per uno sviluppatore di sistemi operativi!
Un altro fattore importante è che è stato preferito passare argomenti nei registri della CPU anziché in pila. A quel tempo, la parola chiave register
era importante per ottimizzare l'ottimizzazione del codice. Era prassi comune dichiarare argomenti di funzione come variabili di registro, per evitare operazioni push / pop. Fortunatamente, puoi passare facilmente i puntatori tramite i registri ma non i valori lunghi come gli array.
Tutto questo era un motivo sufficiente per passare qualsiasi cosa tranne i tipi di build-in per indirizzo invece che per valore. Questo è stato anche il motivo per il vincolo che passa per indirizzo per ogni struttura nel K & R. Iniziale
Naturalmente, oggigiorno, nessuno usa più register
. I COmpiler hanno sovraperformato gli umani nell'uso astuto di questa risorsa. La parola chiave potrebbe essere mantenuta per compatibilità con le versioni precedenti, consentendo una transizione facile.
Sfortunatamente, il passaggio di matrici per valore interromperà la compatibilità all'indietro, poiché la semantica corrente viene utilizzata in milioni di righe di codice. Ad esempio, cosa faresti con:
int a[10];
foo (a, 10); // work on whole array
foo (a+1, 9); // work on part of the array
foo (a+2, 3); // work on a reduced part of the array
foo (a,3); // work on the three first elements
Se prendiamo l'ultimo esempio, non è facile definire un valore passante semantico. Se scrivessi foo (a [3]), sarebbe in conflitto con il modo attuale di passare il 4 ° int di un array.
Se ti piace passare gli array in base al valore, esistono 2 opzioni:
1) la limitazione del passaggio della struttura per indirizzo non è più in vigore (penso che sia dal C99). Quindi puoi fare perfettamente il seguente:
struct wrapper {
int a[10];
};
void foo(struct wrapper w); // pass the whole struct including the array by value.
2) usa C ++ con i vettori, che abilitano tutto ciò che si può sognare di matrici di prima classe.