it-swarm.com.de

Rückgabe eines Arrays in einer Funktion

Ich habe ein Array int arr[5], das an eine Funktion fillarr(int arr[]) übergeben wird:

int fillarr(int arr[])
{
    for(...);
    return arr;
}
  1. Wie kann ich dieses Array zurückgeben?
  2. Wie verwende ich es, sagen wir, ich habe einen Zeiger zurückgegeben, wie soll ich darauf zugreifen?
165
Ismail Marmoush

In diesem Fall kann Ihre Array-Variable arr tatsächlich durch eine implizite Konvertierung als Zeiger auf den Anfang des Arrays des Arrays im Speicher behandelt werden. Diese Syntax, die Sie verwenden:

int fillarr(int arr[])

Ist nur eine Art syntaktischer Zucker. Sie könnten es wirklich ersetzen, und es würde noch funktionieren:

int fillarr(int* arr)

In dem gleichen Sinne ist das, was Sie von Ihrer Funktion zurückgeben möchten, tatsächlich ein Zeiger auf das erste Element im Array:

int* fillarr(int arr[])

Und Sie können es immer noch wie ein normales Array verwenden:

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

C++ - Funktionen können keine C-Arrays nach Wert zurückgeben. Am nächsten ist es, einen Zeiger zurückzugeben. Darüber hinaus wird ein Array-Typ in der Argumentliste einfach in einen Zeiger konvertiert.

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

Sie können dies verbessern, indem Sie für das Argument und für die Rückgabe ein Array verwenden, das den Verfall verhindert:

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

Mit Boost oder C++ 11 ist die Referenzübergabe nur optional und die Syntax ist weniger umwerfend:

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

Die Vorlage array generiert einfach eine struct, die ein Array im C-Stil enthält. Sie können objektorientierte Semantik anwenden und dabei die ursprüngliche Einfachheit des Arrays beibehalten.

94
Potatoswatter

$ 8.3.5/8 Staaten-

"Funktionen dürfen keinen Rückgabetyp eines Typarrays oder einer Funktion haben, obwohl sie einen Rückgabetyp eines Typzeigers oder eine Referenz auf solche Dinge haben können. Es dürfen keine Arrays von Funktionen vorhanden sein, obwohl es auch Arrays von Funktionszeigern geben kann."

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);
}
19
Chubsdad

In C++ 11 können Sie std::array zurückgeben.

#include <array>
using namespace std;

array<int, 5> fillarr(int arr[])
{
    array<int, 5> arr2;
    for(int i=0; i<5; ++i) {
        arr2[i]=arr[i]*2;
    }
    return arr2;
}
15
cubuspl42

die Antwort hängt möglicherweise davon ab, wie Sie diese Funktion verwenden möchten. Lassen Sie uns für die einfachste Antwort entscheiden, dass Sie anstelle eines Arrays einen Vektor wünschen. Vektoren sind schön, weil der Look für die ganze Welt nach langweiligen, gewöhnlichen Werten aussieht, die Sie in normalen Zeigern speichern können. Wir schauen uns andere Optionen an und warum Sie sie danach möchten: 

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

Dies wird genau das tun, was Sie erwarten. Der Vorteil ist, dass std::vector dafür sorgt, dass alles sauber gehandhabt wird. Der Nachteil ist, dass dadurch eine sehr große Datenmenge kopiert wird, wenn das Array groß ist. Tatsächlich kopiert es jedes Element des Arrays zweimal. Zuerst kopiert es den Vektor, damit die Funktion ihn als Parameter verwenden kann. dann kopiert es es erneut, um es an den Anrufer zurückzugeben. Wenn Sie den Vektor selbst verwalten können, können Sie die Aufgaben etwas einfacher erledigen. (es kann ein drittes Mal kopiert werden, wenn der Anrufer es in einer Variablen irgendeiner Art speichern muss, um weitere Berechnungen durchzuführen.)

Es sieht so aus, als würden Sie wirklich eine Sammlung füllen. Wenn Sie keinen bestimmten Grund haben, eine neue Instanz einer Sammlung zurückzugeben, tun Sie dies nicht. Wir können es so machen

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

auf diese Weise erhalten Sie einen Verweis auf das an die Funktion übergebene Array, keine private Kopie davon. Alle Änderungen, die Sie an dem Parameter vornehmen, werden vom Aufrufer angezeigt. Sie können einen Verweis darauf zurückgeben, wenn Sie möchten, aber das ist nicht wirklich eine großartige Idee, da dies in gewisser Weise impliziert, dass Sie etwas anderes als das, was Sie bestanden haben. 

Wenn Sie wirklich eine neue Instanz der Sammlung benötigen, diese jedoch nicht auf dem Stapel (und alle damit verbundenen Kopien) speichern möchten, müssen Sie eine Art Vertrag erstellen, wie diese Instanz behandelt wird. Am einfachsten ist es, einen intelligenten Zeiger zu verwenden, der die referenzierte Instanz aufrechterhält, solange sich jemand daran hält. Es geht sauber weg, wenn es außerhalb des Bereichs liegt. Das würde so aussehen.

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

Die Verwendung von *myArr funktioniert größtenteils identisch mit der Verwendung eines einfachen Vanilla-Vektors. In diesem Beispiel wird auch die Parameterliste geändert, indem das Schlüsselwort const hinzugefügt wird. Jetzt erhalten Sie eine Referenz, ohne sie zu kopieren, aber Sie können sie nicht ändern. Der Anrufer weiß also, dass es derselbe sein wird, wie zuvor, bevor die Funktion dazu kam. 

All dies ist quellfähig, aber idiomatic c ++ funktioniert selten mit Sammlungen als Ganzes. Normalerweise verwenden Sie Iteratoren für diese Sammlungen. das würde eher so aussehen

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

Wenn Sie es nicht gewohnt sind, diesen Stil zu sehen, sieht es etwas komisch aus. 

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

foo 'zeigt jetzt auf den Anfang der geänderten arr

Das Schöne daran ist, dass es auf Vektoren genauso gut funktioniert wie auf einfachen C-Arrays und vielen anderen Arten von Sammlungen

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

Was jetzt sehr viel aussieht wie die einfachen Zeigerbeispiele, die an anderer Stelle in dieser Frage gegeben wurden.

Diese:

int fillarr(int arr[])

wird tatsächlich wie folgt behandelt:

int fillarr(int *arr)

Wenn Sie nun wirklich ein Array zurückgeben möchten, können Sie diese Zeile in ändern

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

Es gibt nicht wirklich ein Array zurück. Sie geben einen Zeiger auf den Anfang der Array-Adresse zurück.

Denken Sie jedoch daran, dass Sie beim Übergeben des Arrays nur einen Zeiger übergeben. Wenn Sie also die Array-Daten ändern, ändern Sie tatsächlich die Daten, auf die der Zeiger zeigt. Bevor Sie also das Array übergeben, müssen Sie feststellen, dass Sie das geänderte Ergebnis bereits außen haben.

z.B.

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;
}

Ich schlage vor, Sie möchten eine Länge in Ihre Fillarr-Funktion wie folgt einfügen.

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

Auf diese Weise können Sie die Länge verwenden, um das Array auf seine Länge zu füllen, unabhängig davon, was es ist.

Um es richtig zu benutzen. Mach so etwas:

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.

Wenn Sie das Array nur auf einige Standardwerte setzen möchten, können Sie die integrierte Memset-Funktion verwenden.

so etwas wie: memset ((int *) & arr, 5, sizeof (int));

Ich bin zwar beim Thema. Sie sagen, Sie verwenden C++. Schauen Sie sich die Verwendung von STL-Vektoren an. Ihr Code ist wahrscheinlich robuster.

Es gibt viele Tutorials. Hier ist eine, die Ihnen eine Vorstellung davon gibt, wie Sie sie verwenden können. http://www.yolinux.com/TUTORIALS/LinuxTutorialC++STL.html

8
Matt

um ein Array aus einer Funktion zurückzugeben, definieren wir dieses Array in einer Struktur: Es sieht also ungefähr so ​​aus

struct Marks{
   int list[5];
}

Lassen Sie uns nun Variablen der Typstruktur erstellen. 

typedef struct Marks marks;
marks marks_list;

Wir können array auf folgende Weise an eine Funktion übergeben und dieser einen Wert zuweisen:

void setMarks(int marks_array[]){
   for(int i=0;i<sizeof(marks_array)/sizeof(int);i++)
       marks_list.list[i]=marks_array[i];
}

Wir können das Array auch zurückgeben. Um das Array zurückzugeben, sollte der Rückgabetyp der Funktion vom Strukturtyp sein, dh Markierungen. Dies liegt daran, dass wir in Wirklichkeit die Struktur übergeben, die das Array enthält. Der endgültige Code kann also so aussehen.

marks getMarks(){
 return marks_list;
}
5
Sandeep

Dies ist eine ziemlich alte Frage, aber ich werde meine 2 Cent einbringen, da es viele Antworten gibt, aber keine, die alle möglichen Methoden auf eine klare und prägnante Weise zeigt (nicht sicher über das kurze Bit, da dies eine etwas aus der Hand. TL; DR ????).

Ich gehe davon aus, dass das OP das übergebene Array zurückgeben wollte, ohne es zu kopieren, um dies direkt an den Aufrufer zu übergeben, um an eine andere Funktion übergeben zu werden, damit der Code schöner aussieht.

Um ein Array wie dieses zu verwenden, soll es jedoch in einen Zeiger zerfallen und der Compiler es like ein Array behandeln lassen. Dies kann zu subtilen Fehlern führen, wenn Sie ein Array wie übergeben, wobei die Funktion erwartet, dass es 5 Elemente enthält, der Anrufer jedoch tatsächlich eine andere Nummer übergibt.

Es gibt einige Möglichkeiten, wie Sie besser damit umgehen können. Übergeben Sie einen std::vector oder std::array (nicht sicher, ob sich std::array im Jahr 2010 befand, als die Frage gestellt wurde). Sie können das Objekt dann als Referenz übergeben, ohne das Objekt zu kopieren oder zu verschieben.

std::array<int, 5>& fillarr(std::array<int, 5>& 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<int>& fillarr(std::vector<int>& arr)
{
    for(auto it = arr.begin(); it != arr.end(); ++it)
    { /* do stuff */ }
    return arr;
}

Wenn Sie jedoch darauf bestehen, mit C-Arrays zu spielen, verwenden Sie eine Vorlage, in der die Informationen über die Anzahl der Elemente im Array gespeichert werden.

template <size_t N>
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;
}

Nur sieht das hässlich aus und ist sehr schwer zu lesen. Ich benutze jetzt etwas, um bei dem zu helfen, was 2010 nicht in der Nähe war, das ich auch für Funktionszeiger verwende:

template <typename T>
using type_t = T;

template <size_t N>
type_t<int(&)[N]> fillarr(type_t<int(&)[N]> 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;
}

Dies verschiebt den Typ dahin, wo man ihn erwarten würde, wodurch er weit lesbarer wird. Die Verwendung einer Vorlage ist natürlich überflüssig, wenn Sie nur 5 Elemente verwenden. Sie können sie natürlich hart codieren:

type_t<int(&)[5]> fillarr(type_t<int(&)[5]> 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;
}

Wie gesagt, mein type_t<>-Trick hätte zu dem Zeitpunkt, als diese Frage gestellt wurde, nicht funktioniert. Das Beste, was man sich damals erhoffen konnte, war die Verwendung eines Typs in einer Struktur:

template<typename T>
struct type
{
  typedef T type;
};

typename type<int(&)[5]>::type fillarr(typename type<int(&)[5]>::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;
}

Das sieht wieder ziemlich hässlich aus, ist aber zumindest lesbarer, obwohl typename damals je nach Compiler optional gewesen sein kann, was zu folgendem Ergebnis führt:

type<int(&)[5]>::type fillarr(type<int(&)[5]>::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;
}

Und dann hätten Sie natürlich einen bestimmten Typ angeben können, anstatt meinen Helfer zu verwenden.

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;
}

Damals gab es die freien Funktionen std::begin() und std::end() nicht, obwohl sie leicht implementiert werden konnten. Dies hätte es erlaubt, das Array sicherer zu durchlaufen, da dies für ein C-Array Sinn macht, nicht aber für einen Zeiger.

Wenn Sie auf das Array zugreifen, können Sie es entweder an eine andere Funktion übergeben, die denselben Parametertyp verwendet, oder einen Aliasnamen verwenden (was wenig Sinn macht, da Sie bereits das Original in diesem Bereich haben). Der Zugriff auf eine Arrayreferenz ist genauso wie der Zugriff auf das ursprüngliche Array.

void other_function(type_t<int(&)[5]> x) { /* do something else */ }

void fn()
{
    int array[5];
    other_function(fillarr(array));
}

oder

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
}

Zusammenfassend lässt sich sagen, dass es am besten ist, ein Array nicht in einen Zeiger zerfallen zu lassen, wenn Sie darüber iterieren möchten. Dies ist nur eine schlechte Idee, da der Compiler Sie davor bewahrt, sich in den Fuß zu schießen, und Ihr Code wird schwerer lesbar. Versuchen Sie immer, dem Compiler zu helfen, indem Sie die Typen so lange wie möglich beibehalten, es sei denn, Sie haben einen guten Grund, dies nicht zu tun.

Bearbeiten

Der Vollständigkeit halber können Sie zulassen, dass der Zeiger zu einem Zeiger degradiert wird. Dies entkoppelt jedoch das Array von der Anzahl der Elemente, die es enthält. Dies wird häufig in C/C++ durchgeführt und wird normalerweise durch Übergeben der Anzahl der Elemente im Array gemindert. Der Compiler kann Ihnen jedoch nicht helfen, wenn Sie einen Fehler machen und den falschen Wert an die Anzahl der Elemente übergeben.

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

Anstatt die Größe zu übergeben, können Sie den Endzeiger übergeben, der auf einen Punkt hinter dem Ende Ihres Arrays zeigt. Dies ist nützlich, da es etwas ist, das näher an den Standardalgorithmen ist, die einen Anfangs- und einen Endzeiger enthalten.

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

Alternativ können Sie dokumentieren, dass diese Funktion nur 5 Elemente umfasst, und hoffen, dass der Benutzer Ihrer Funktion nichts Dummes tut.

// 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;
}

Beachten Sie, dass der Rückgabewert seinen ursprünglichen Typ verloren hat und zu einem Zeiger degradiert wird. Aus diesem Grund sind Sie jetzt alleine, um sicherzustellen, dass Sie das Array nicht überfahren.

Sie könnten einen std::pair<int*, int*> übergeben, den Sie für Anfang und Ende verwenden können, um ihn herumzugeben, aber dann sieht es wirklich nicht mehr wie ein Array aus.

std::pair<int*, int*> fillarr(std::pair<int*, int*> 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];
}

oder

void other_function(std::pair<int*, int*> 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])));
}

Komischerweise ist dies sehr ähnlich wie std::initializer_list (c ++ 11), aber in diesem Zusammenhang funktionieren sie nicht.

4
Adrian

der einfachste Weg, dies zu tun, ist die Rückgabe per Verweis, auch wenn Sie nicht das Zeichen "&" schreiben, wird es automatisch als Verweis zurückgegeben

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

  }
3
nada
int *fillarr(int arr[])

Sie können das Ergebnis trotzdem gerne verwenden

int *returned_array = fillarr(some_other_array);
if(returned_array[0] == 3)
    do_important_cool_stuff();
2
Daniel

und was ist mit:

int (*func())
{
    int *f = new int[10] {1,2,3};

    return f;
}

int fa[10] = { 0 };
auto func2() -> int (*) [10]
{
    return &fa;
}
0
Alexandr

Wenn Sie ein Array innerhalb einer Funktion übergeben, wird der Zeiger auf das ursprüngliche Array im Funktionsparameter übergeben. Daher werden die Änderungen, die an dem Array innerhalb dieser Funktion vorgenommen wurden, tatsächlich im ursprünglichen Array vorgenommen. 

#include <iostream>

using namespace std;

int* func(int ar[])
{
    for(int i=0;i<100;i++) 
        ar[i]=i;
    int *ptr=ar;
    return ptr;
}


int main() {
    int *p;
    int y[100]={0};    
    p=func(y);

    for(int i=0;i<100;i++) 
        cout<<i<<" : "<<y[i]<<'\n';
}

Führen Sie es aus und Sie werden die Änderungen sehen

0
Abhishek gaur

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

C++ erlaubt nicht, ein gesamtes Array als Argument an eine Funktion zurückzugeben. Sie können jedoch einen Zeiger auf ein Array zurückgeben, indem Sie den Namen des Arrays ohne Index angeben.

  1. Wenn Sie ein eindimensionales Array von einer Funktion zurückgeben möchten, müssen Sie eine Funktion deklarieren, die einen Zeiger zurückgibt, wie im folgenden Beispiel:
int * myFunction()    {
   .
   .
   .
}
  1. C++ empfiehlt nicht, die Adresse einer lokalen Variablen außerhalb der Funktion zurückzugeben, sodass Sie die lokale Variable als statische Variable definieren müssen.

Wenn Sie diese Regeln auf die aktuelle Frage anwenden, können Sie das Programm wie folgt schreiben:

# include <iostream>

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;
 }

Die Ausgabe wird sein: 

p[0]=0
p[1]=1
p[2]=2
p[3]=3
p[4]=4
0
MAQ
template<typename T, size_t N>
using ARR_REF = T (&)[N];

template <typename T, size_t N>
ARR_REF<T,N> ArraySizeHelper(ARR_REF<T,N> arr);

#define arraysize(arr) sizeof(ArraySizeHelper(arr))
0
Nozama