Restituisce array in una funzione

Ho un array int arr[5] che viene passato a una funzione fillarr(int arr[]) :

 int fillarr(int arr[]) { for(...); return arr; } 
  1. Come posso restituire quell’array?
  2. Come posso usarlo, dire che ho restituito un puntatore come posso accedervi?

In questo caso, l’array variable arr può anche essere trattato come un puntatore all’inizio del blocco dell’array in memoria, mediante una conversione implicita. Questa syntax che stai utilizzando:

 int fillarr(int arr[]) 

È una specie di zucchero sintattico. Potresti davvero sostituirlo con questo e funzionerebbe ancora:

 int fillarr(int* arr) 

Quindi, nello stesso senso, ciò che si desidera restituire dalla propria funzione è in realtà un puntatore al primo elemento dell’array:

 int* fillarr(int arr[]) 

E sarai comunque in grado di usarlo proprio come faresti con un normale array:

 int main() { int y[10]; int *a = fillarr(y); cout << a[0] << endl; } 

Le funzioni C ++ non possono restituire gli array in stile C in base al valore. La cosa più vicina è restituire un puntatore. Inoltre, un tipo di array nell’elenco di argomenti viene semplicemente convertito in un puntatore.

 int *fillarr( int arr[] ) { // arr "decays" to type int * return arr; } 

Puoi migliorarlo usando i riferimenti di un array per l’argomento e il ritorno, che impedisce il decadimento:

 int ( &fillarr( int (&arr)[5] ) )[5] { // no decay; argument must be size 5 return arr; } 

Con Boost o C ++ 11, il pass-by-reference è solo opzionale e la syntax è meno impegnativa:

 array< int, 5 > &fillarr( array< int, 5 > &arr ) { return arr; // "array" being boost::array or std::array } 

Il modello di array genera semplicemente una struct contenente un array in stile C, quindi è ansible applicare la semantica orientata agli oggetti mantenendo la semplicità originale dell’array.

$ 8.3.5 / 8 stati-

“Le funzioni non devono avere un tipo di ritorno di tipo matrice o funzione, sebbene possano avere un tipo di puntatore di ritorno o un riferimento a tali elementi. Non ci devono essere matrici di funzioni, anche se possono esserci matrici di indicatori di funzioni.”

 int (&fn1(int (&arr)[5]))[5]{ // declare fn1 as returning refernce to array return arr; } int *fn2(int arr[]){ // declare fn2 as returning pointer to array return arr; } int main(){ int buf[5]; fn1(buf); fn2(buf); } 

In C ++ 11, puoi restituire std::array .

 #include  using namespace std; array fillarr(int arr[]) { array arr2; for(int i=0; i<5; ++i) { arr2[i]=arr[i]*2; } return arr2; } 

la risposta potrebbe dipendere un po ‘da come pensi di usare quella funzione. Per la risposta più semplice, decidiamo che invece di un array, quello che vuoi veramente è un vettore. I vettori sono carini perché il look per tutto il mondo assomiglia a valori noiosi e ordinari che puoi memorizzare in puntatori regolari. Vedremo altre opzioni e perché le vuoi in seguito:

 std::vector fillarr( std::vector arr ) { // do something return arr; } 

Questo farà esattamente quello che ti aspetti che faccia. Il lato positivo è che std::vector si occupa di assicurarsi che tutto sia gestito in modo pulito. il rovescio della medaglia è che questo copia una grande quantità di dati, se l’array è grande. In effetti copia due volte ogni elemento dell’array. per prima cosa copia il vettore in modo che la funzione possa usarlo come parametro. quindi lo copia nuovamente per restituirlo al chiamante. Se riesci a gestire da solo la gestione del vettore, puoi fare le cose un po ‘più facilmente. (può copiarlo una terza volta se il chiamante deve memorizzarlo in una variabile di qualche tipo per fare più calcoli)

Sembra che quello che stai davvero cercando di fare sia compilare una raccolta. se non si dispone di un motivo specifico per restituire una nuova istanza di una raccolta, non farlo. possiamo farlo in questo modo

 void fillarr(std::vector & arr) { // modify arr // don't return anything } 

in questo modo si ottiene un riferimento alla matrice passata alla funzione, non una sua copia privata. eventuali modifiche apportate al parametro vengono visualizzate dal chiamante. Potresti restituire un riferimento ad esso, se vuoi, ma non è proprio una grande idea, dal momento che implica che stai ottenendo qualcosa di diverso da ciò che hai passato.

Se hai davvero bisogno di una nuova istanza della collezione, ma vuoi evitare di averla in pila (e tutta la copia che comporta), devi creare un qualche tipo di contratto per come viene gestita quell’istanza. il modo più semplice per farlo è quello di utilizzare un puntatore intelligente, che mantiene l’istanza referenziata fino a quando qualcuno vi sta trattenendo. Va via in modo pulito se va fuori dal campo di applicazione. Sarebbe come questo.

 std::auto_ptr > fillarr( const std::vector & arr) { std::auto_ptr > myArr(new std::vector); // do stuff with arr and *myArr return myArr; } 

Per la maggior parte, usando *myArr funziona in modo identico all’utilizzo di un semplice vettore di vaniglia. Questo esempio modifica anche l’elenco dei parametri aggiungendo la parola chiave const . Ora si ottiene un riferimento senza copiarlo, ma non è ansible modificarlo, in modo che il chiamante sappia che sarà lo stesso di prima della funzione.

Tutto ciò è magnifico, ma il c ++ idiomatico raramente funziona con le collezioni nel loro complesso. Più normalmente, userete gli iteratori su quelle raccolte. sembrerebbe qualcosa di più simile a questo

 template  Iterator fillarr(Iterator arrStart, Iterator arrEnd) { Iterator arrIter = arrStart; for(;arrIter <= arrEnd; arrIter++) ;// do something return arrStart; } 

Usarlo sembra un po 'strano se non sei abituato a vedere questo stile.

 vector arr; vector::iterator foo = fillarr(arr.begin(), arr.end()); 

foo now 'indica' l'inizio arr modificato.

La cosa veramente bella di questo è che funziona altrettanto bene sul vettore come sui semplici array C e su molti altri tipi di raccolta, per esempio

 int arr[100]; int *foo = fillarr(arr, arr+100); 

Che ora assomiglia moltissimo agli esempi di puntatori semplici forniti altrove in questa domanda.

Questo:

 int fillarr(int arr[]) 

è in realtà trattato allo stesso modo di:

 int fillarr(int *arr) 

Ora se vuoi veramente restituire un array puoi cambiare quella linea in

 int * fillarr(int arr[]){ // do something to arr return arr; } 

In realtà non sta restituendo un array. stai restituendo un puntatore all’inizio dell’indirizzo dell’array.

Ma ricorda quando passi nell’array, stai passando solo un puntatore. Quindi, quando modifichi i dati dell’array, stai effettivamente modificando i dati a cui punta il puntatore. Quindi, prima di passare nell’array, devi capire che hai già all’esterno il risultato modificato.

per esempio

 int fillarr(int arr[]){ array[0] = 10; array[1] = 5; } int main(int argc, char* argv[]){ int arr[] = { 1,2,3,4,5 }; // arr[0] == 1 // arr[1] == 2 etc int result = fillarr(arr); // arr[0] == 10 // arr[1] == 5 return 0; } 

Ti suggerisco di prendere in considerazione l’idea di inserire una lunghezza nella tua funzione fillarr come questa.

 int * fillarr(int arr[], int length) 

In questo modo puoi usare la lunghezza per riempire l’array alla sua lunghezza, non importa quale sia.

Per usarlo correttamente. Fai qualcosa del genere:

 int * fillarr(int arr[], int length){ for (int i = 0; i < length; ++i){ // arr[i] = ? // do what you want to do here } return arr; } // then where you want to use it. int arr[5]; int *arr2; arr2 = fillarr(arr, 5); // at this point, arr & arr2 are basically the same, just slightly // different types. You can cast arr to a (char*) and it'll be the same. 

Se tutto ciò che si vuole fare è impostare la matrice su alcuni valori predefiniti, considerare l'uso della funzione memset integrata.

qualcosa come: memset ((int *) e arr, 5, sizeof (int));

Mentre io sono sull'argomento però. Dici che stai usando C ++. Dai un'occhiata ai vettori di stl. È probabile che il tuo codice sia più robusto.

Ci sono molti tutorial. Ecco uno che ti dà un'idea di come usarli. http://www.yolinux.com/TUTORIALS/LinuxTutorialC++STL.html

per restituire un array da una funzione, definiamo quell’array in una struttura; Quindi sembra qualcosa del genere

 struct Marks{ int list[5]; } 

Ora creiamo le variabili della struttura del tipo.

 typedef struct Marks marks; marks marks_list; 

Possiamo passare array a una funzione nel modo seguente e assegnare un valore ad esso:

 void setMarks(int marks_array[]){ for(int i=0;i 

Possiamo anche restituire l'array. Per restituire l'array, il tipo restituito della funzione dovrebbe essere di tipo di struttura ovvero segni. Questo perché in realtà stiamo passando la struttura che contiene l'array. Quindi il codice finale potrebbe assomigliare a questo.

 marks getMarks(){ return marks_list; } 

Questa è una domanda abbastanza vecchia, ma inserirò i miei 2 cent perché ci sono molte risposte, ma nessuna mostra tutti i metodi possibili in modo chiaro e conciso (non sono sicuro del bit conciso, dato che ha ottenuto un un po ‘fuori mano. TL; DR 😉).

Suppongo che l’OP volesse restituire l’array che è stato passato senza copiarlo, poiché alcuni mezzi per passare direttamente questo al chiamante da passare a un’altra funzione per rendere il codice più carino.

Tuttavia, usare un array come questo è lasciarlo decadere in un puntatore e fare in modo che il compilatore lo tratti come un array. Questo può portare a bug sottili se si passa in un array come, con la funzione che prevede che avrà 5 elementi, ma il chiamante passa effettivamente in qualche altro numero.

Esistono alcuni modi per gestirli meglio. Passa in un file std::vector o std::array (non è sicuro se std::array fosse nel 2010 quando è stata posta la domanda). È quindi ansible passare l’object come riferimento senza copiare / spostare l’object.

 std::array& fillarr(std::array& arr) { // (before c++11) for(auto it = arr.begin(); it != arr.end(); ++it) { /* do stuff */ } // Note the following are for c++11 and higher. They will work for all // the other examples below except for the stuff after the Edit. // (c++11 and up) for(auto it = std::begin(arr); it != std::end(arr); ++it) { /* do stuff */ } // range for loop (c++11 and up) for(auto& element : arr) { /* do stuff */ } return arr; } std::vector& fillarr(std::vector& arr) { for(auto it = arr.begin(); it != arr.end(); ++it) { /* do stuff */ } return arr; } 

Tuttavia, se insisti a giocare con i C array, usa un modello che manterrà le informazioni sul numero di elementi nell’array.

 template  int(&fillarr(int(&arr)[N]))[N] { // N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0]) for(int* it = arr; it != arr + N; ++it) { /* do stuff */ } return arr; } 

Tranne che sembra brutto e molto difficile da leggere. Ora uso qualcosa per aiutare ciò che non era nel 2010, che uso anche per i puntatori di funzione:

 template  using type_t = T; template  type_t fillarr(type_t arr) { // N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0]) for(int* it = arr; it != arr + N; ++it) { /* do stuff */ } return arr; } 

Questo sposta il tipo dove ci si aspetterebbe che fosse, rendendo questo molto più leggibile. Ovviamente, l’uso di un modello è superfluo se non si intende utilizzare altro che 5 elementi, quindi è ansible ovviamente codificarlo:

 type_t fillarr(type_t arr) { // Prefer using the compiler to figure out how many elements there are // as it reduces the number of locations where you have to change if needed. for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it) { /* do stuff */ } return arr; } 

Come ho detto, il mio type_t<> trucco non avrebbe funzionato nel momento in cui è stata posta questa domanda. Il meglio che potevi sperare era usare un tipo in una struttura:

 template struct type { typedef T type; }; typename type::type fillarr(typename type::type arr) { // Prefer using the compiler to figure out how many elements there are // as it reduces the number of locations where you have to change if needed. for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it) { /* do stuff */ } return arr; } 

Il che inizia a sembrare piuttosto brutto di nuovo, ma almeno è ancora più leggibile, anche se il typename potrebbe essere stato opzionale a quel punto a seconda del compilatore, risultando in:

 type::type fillarr(type::type arr) { // Prefer using the compiler to figure out how many elements there are // as it reduces the number of locations where you have to change if needed. for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it) { /* do stuff */ } return arr; } 

E poi ovviamente avresti potuto specificare un tipo specifico, piuttosto che usare il mio aiutante.

 typedef int(&array5)[5]; array5 fillarr(array5 arr) { // Prefer using the compiler to figure out how many elements there are // as it reduces the number of locations where you have to change if needed. for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it) { /* do stuff */ } return arr; } 

Allora, le funzioni gratuite std::begin() e std::end() non esistevano, anche se potevano essere facilmente implementate. Ciò avrebbe consentito di eseguire iterazioni sull’array in un modo più sicuro poiché hanno senso su un array C, ma non su un puntatore.

Per quanto riguarda l’accesso all’array, è ansible passarlo a un’altra funzione che assume lo stesso tipo di parametro o creare un alias (che non avrebbe molto senso in quanto l’originale è già presente in tale ambito). L’accesso a un riferimento di array è come l’accesso alla matrice originale.

 void other_function(type_t x) { /* do something else */ } void fn() { int array[5]; other_function(fillarr(array)); } 

o

 void fn() { int array[5]; auto& array2 = fillarr(array); // alias. But why bother. int forth_entry = array[4]; int forth_entry2 = array2[4]; // same value as forth_entry } 

Per riassumere, è meglio non consentire il decadimento dell’array in un puntatore se si intende iterare su di esso. È solo una ctriggers idea in quanto impedisce al compilatore di proteggerti dal spararti ai piedi e rende più difficile leggere il tuo codice. Cerca sempre di aiutare il compilatore ad aiutarti mantenendo i tipi il più a lungo ansible a meno che tu non abbia una buona ragione per non farlo.

modificare

Oh, e per completezza, puoi permetterlo di degradarsi a un puntatore, ma questo disaccoppia l’array dal numero di elementi che contiene. Questo viene fatto molto in C / C ++ e di solito viene mitigato passando il numero di elementi nella matrice. Tuttavia, il compilatore non può aiutarti se commetti un errore e passi il valore sbagliato al numero di elementi.

 // separate size value int* fillarr(int* arr, size_t size) { for(int* it = arr; it != arr + size; ++it) { /* do stuff */ } return arr; } 

Invece di passare la dimensione, puoi passare il puntatore finale, che punterà a uno oltre la fine dell’array. Questo è utile in quanto rende qualcosa che è più vicino agli algoritmi std, che prendono un puntatore inizio e fine, ma ciò che ritorni è ora solo qualcosa che devi ricordare.

 // separate end pointer int* fillarr(int* arr, int* end) { for(int* it = arr; it != end; ++it) { /* do stuff */ } return arr; } 

In alternativa, puoi documentare che questa funzione richiede solo 5 elementi e sperare che l’utente della tua funzione non faccia nulla di stupido.

 // I document that this function will ONLY take 5 elements and // return the same array of 5 elements. If you pass in anything // else, may nazal demons exit thine nose! int* fillarr(int* arr) { for(int* it = arr; it != arr + 5; ++it) { /* do stuff */ } return arr; } 

Si noti che il valore restituito ha perso il suo tipo originale e viene degradato a un puntatore. Per questo motivo, ora sei da solo per assicurarti di non sovraccaricare l’array.

Potresti passare una std::pair , che puoi usare per iniziare e finire e passarla, ma poi smette davvero di sembrare una matrice.

 std::pair fillarr(std::pair arr) { for(int* it = arr.first; it != arr.second; ++it) { /* do stuff */ } return arr; // if you change arr, then return the original arr value. } void fn() { int array[5]; auto array2 = fillarr(std::make_pair(&array[0], &array[5])); // Can be done, but you have the original array in scope, so why bother. int fourth_element = array2.first[4]; } 

o

 void other_function(std::pair array) { // Can be done, but you have the original array in scope, so why bother. int fourth_element = array2.first[4]; } void fn() { int array[5]; other_function(fillarr(std::make_pair(&array[0], &array[5]))); } 

Abbastanza divertente, è molto simile a come funziona std::initializer_list (c ++ 11), ma non funzionano in questo contesto.

il modo più semplice per farlo è restituirlo per riferimento, anche se non scrivi il simbolo “&”, viene automaticamente restituito per riferimento

  void fillarr(int arr[5]) { for(...); } 
 int *fillarr(int arr[]) 

Puoi ancora usare il risultato come

 int *returned_array = fillarr(some_other_array); if(returned_array[0] == 3) do_important_cool_stuff(); 
 template using ARR_REF = T (&)[N]; template  ARR_REF ArraySizeHelper(ARR_REF arr); #define arraysize(arr) sizeof(ArraySizeHelper(arr)) 

Fonte: https://www.tutorialspoint.com/cplusplus/cpp_return_arrays_from_functions.htm

C ++ non consente di restituire un intero array come argomento di una funzione. Tuttavia, è ansible restituire un puntatore a un array specificando il nome dell’array senza un indice.

  1. Se si desidera restituire una matrice a dimensione singola da una funzione, è necessario dichiarare una funzione che restituisce un puntatore come nell’esempio seguente:
 int * myFunction() { . . . } 
  1. C ++ non sostiene di restituire l’indirizzo di una variabile locale all’esterno della funzione in modo da dover definire la variabile locale come variabile statica.

Applicando queste regole alla domanda corrente, possiamo scrivere il programma come segue:

 # include  using namespace std; int * fillarr( ); int main () { int *p; p = fillarr(); for ( int i = 0; i < 5; i++ ) cout << "p[" << i << "] : "<< *(p + i) << endl; return 0; } int * fillarr( ) { static int arr[5]; for (int i = 0; i < 5; ++i) arr[i] = i; return arr; } 

L'uscita sarà:

 p[0]=0 p[1]=1 p[2]=2 p[3]=3 p[4]=4 

e che mi dici di:

 int (*func()) { int *f = new int[10] {1,2,3}; return f; } int fa[10] = { 0 }; auto func2() -> int (*) [10] { return &fa; } 

Basta definire un tipo [] come valore di ritorno, come:

  private string[] functionReturnValueArray(string one, string two) { string[] x = {one, two}; x[0] = "a"; x[1] = "b"; return x; } 

. . . chiamata di funzione:

 string[] y; y = functionReturnValueArray(stringOne, stringTwo)