Perché non possiamo passare gli array a funzionare in base al valore?

Apparentemente, possiamo passare istanze di classi complesse a funzioni, ma perché non possiamo passare gli array alle funzioni?

L’origine è storica. Il problema è che la regola “decadimento degli array in puntatori, quando passata a una funzione” è semplice.

Copiare gli array sarebbe un po ‘complicato e non molto chiaro, poiché il comportamento cambierebbe per diversi parametri e dichiarazioni di funzioni diverse.

Tieni presente che puoi ancora eseguire un passaggio indiretto per valore:

 struct A { int arr[2]; }; void func(struct A); 

Ecco un’altra prospettiva: non c’è un singolo tipo “array” in C. Piuttosto, T[N] è un tipo diverso per ogni N Quindi T[1] , T[2] , ecc. Sono tutti tipi diversi .

In C non c’è sovraccarico di funzioni, e quindi l’unica cosa sensata che avresti potuto permettere sarebbe una funzione che prende (o restituisce) un singolo tipo di array :

 void foo(int a[3]); // hypothetical 

Presumibilmente, ciò è stato considerato molto meno utile della decisione effettiva di trasformare tutti gli array in un puntatore al primo elemento e richiedere all’utente di comunicare le dimensioni con altri mezzi. Dopotutto, quanto sopra potrebbe essere riscritto come:

 void foo(int * a) { static const unsigned int N = 3; /* ... */ } 

Quindi non c’è perdita di potere espressivo, ma un enorme guadagno di generalità.

Nota che questo non è diverso in C ++, ma la generazione di codice basata su template ti permette di scrivere una funzione basata su template foo(T (&a)[N]) , dove N è dedotto per te – ma questo significa solo che puoi creare un’intera famiglia di funzioni distinte e diverse , una per ogni valore di N

Come caso estremo, immagina che avresti bisogno di due funzioni print6(const char[6]) e print12(const char[12]) per dire print6("Hello") e print12("Hello World") se non lo facessi vuoi decadere gli array ai puntatori o altrimenti dovresti aggiungere una conversione esplicita, print_p((const char*)"Hello World") .

Rispondendo a una domanda molto vecchia, dato che Question è market con C ++ che si aggiunge solo a fini di completamento, possiamo usare std :: array e passa array a funzioni per valore o per riferimento che fornisce protezione contro l’accesso agli indici fuori limite:

qui sotto è il campione:

 #include  #include  //pass array by reference template void fill_array(std::array& arr){ for(int idx = 0; idx < arr.size(); ++idx) arr[idx] = idx*idx; } //pass array by value template void print_array(std::array arr){ for(int idx = 0; idx < arr.size(); ++idx) std::cout << arr[idx] << std::endl; } int main() { std::array arr; fill_array(arr); print_array(arr); //use different size std::array arr2; fill_array(arr2); print_array(arr2); } 

Il motivo per cui non è ansible passare un array in base al valore è perché non esiste un modo specifico per tenere traccia delle dimensioni di un array in modo tale che la logica di chiamata di funzione possa sapere quanta memoria allocare e cosa copiare. È ansible passare un’istanza di class perché le classi hanno costruttori. Gli array no

Stai passando per valore: il valore del puntatore all’array. Ricorda che usare la notazione delle parentesi quadre in C è semplicemente una scorciatoia per de-referenziare un puntatore. ptr [2] significa * (ptr + 2).

Lasciando cadere le parentesi si ottiene un puntatore all’array, che può essere passato per valore a una funzione:

 int x[2] = {1, 2}; int result; result = DoSomething(x); 

Vedere l’ elenco dei tipi nelle specifiche ANSI C. Gli array non sono tipi primitivi, ma costruiti da una combinazione di puntatori e operatori. (Non mi consente di inserire un altro link, ma la costruzione è descritta sotto “Derivazione del tipo di matrice”.)

Summery:

  1. Passando l’ indirizzo del primo elemento dell’array &a = a = &(a[0])
  2. Nuovo puntatore (nuovo puntatore, nuovo indirizzo , 4 byte, nella memoria)
  3. Indica la stessa posizione di memoria , in tipi diversi .

Esempio 1:

 void by_value(bool* arr) // pointer_value passed by value { arr[1] = true; arr = NULL; // temporary pointer that points to original array } int main() { bool a[3] = {}; cout << a[1] << endl; // 0 by_value(a); cout << a[1] << endl; // 1 !!! } 

indirizzi:

 [main] a = 0046FB18 // **Original** &a = 0046FB18 // **Original** [func] arr = 0046FB18 // **Original** &arr = 0046FA44 // TempPTR [func] arr = NULL &arr = 0046FA44 // TempPTR 

Esempio 2:

 void by_value(bool* arr) { cout << &arr << arr; // &arr != arr } int main() { bool a[3] = {}; cout << &a << a; // &a == a == &a[0] by_value(arr); } 

indirizzi

 Prints: [main] 0046FB18 = 0046FB18 [func] 0046FA44 != 0046FB18 

Notare che:

  1. & (required-lvalue): lvalue -to-> rvalue
  2. Array Decay: il nuovo puntatore (temporaneo) punta a (per valore) indirizzo di matrice

leggi di più:

rvalue

Array Decay

L’equivalente sarebbe quello di fare prima una copia dell’array e poi passarlo alla funzione (che può essere molto inefficiente per i grandi array).

Oltre a questo, direi che è per ragioni storiche, vale a dire che non è ansible passare gli array in base al valore in C.

La mia ipotesi è che il ragionamento alla base del fatto che NON introducendo matrici di valore in C ++ fosse che gli oggetti erano moderatamente dimensionati rispetto agli array.

Come sottolineato da delnan, quando si usa std::vector si possono effettivamente passare oggetti di tipo array a funzioni per valore.

in realtà, un puntatore all’array viene passato per valore , usando quel puntatore all’interno della funzione chiamata si avrà la sensazione che l’array sia passato per riferimento che è sbagliato. prova a cambiare il valore nel puntatore dell’array in modo che punti ad un altro array nella tua funzione e scoprirai che l’array originale non è stato influenzato, il che significa che l’array non viene passato per riferimento.