Problem mit Array als Rückgabe



  • Hallo zusammen,
    ich habe eine Funktion, die ein Array über Pointer zurückgeben soll. Das sieht so aus:

    double* calc_R(int* arr) //arr hat 6 Elemente
    {
     double Ri[6];
     for (int i=0; i<6; i++)
       Ri[i] = arr[i] / SENSI; //SENSI ist eine Konstante (50)
     return Ri;
    }
    
    int main()
    {
     int num[6] = {100,150,200,250,300,350};
     double* R = calc_R(num);
     for (int i=0; i<6; i++)
       cout << R[i] << endl;
    
     return 0;
    }
    

    Es wird kompiliert, doch es kommen bis auf den ersten völlig falsche Werte raus. Richt wäre 2,3,4,5,6,7.

    Wo liegt mein Denkfehler?

    Vielen Dank
    lg, freakC++



  • Du darfst keine Adresse auf ein lokales Objekt zurückgeben.

    #include <vector>
    
    std::vector<double> calc_R(int* arr) //arr hat 6 Elemente
    {
     std::vector<double> Ri(6);
     for (int i=0; i<6; i++)
       Ri[i] = arr[i] / SENSI; //SENSI ist eine Konstante (50)
     return Ri;
    }
    
    int main()
    {
     int num[6] = {100,150,200,250,300,350};
     std::vector<double> R = calc_R(num);
     for (int i=0; i<6; i++)
       cout << R[i] << endl;
    
     return 0;
    }
    

    So könnte es gehen (hab's nicht übersetzt)

    Lars



  • return Ri; // [Warning] address of local variable `Ri' returned
    

    Wenn die Funktion zu Ende ist, wird der Speicherplatz des Arrays wieder freigegeben und ist somit nicht mehr geschützt.
    Irgendetwas anderes benutzt halt danach diesen Speicherbereich und du bekommst Murks raus.

    Machen kannst du es z.B. so, aber diese Lösung ist mit Sicherheit nicht als gut zu bewerten:

    #include <iostream>
    using namespace std;
    
    double* calc_R(int* arr) // arr hat 6 Elemente
    {
        /*
            dynamische Speicherverwaltung...
                Speicherplatz wird am Ende der Funktion nicht freigegeben
                und bleibt somit geschützt.
        */
        double* Ri = new double[6];
        for (int i=0; i<6; i++)
            Ri[i] = arr[i] / 50; // SENSI ist eine Konstante (50)
    
        return Ri;
    }
    
    int main()
    {
        int num[6] = {100,150,200,250,300,350};
        double* R = calc_R(num);
    
        for(int i=0; i<6; i++)
            cout << R[i] << endl;
    
        delete[] R;
    
        return 0;
    }
    


  • Hallo,
    ok, vielen Dank! Das ist einleuchtend. Wenn ich nun ohne vector arbeiten möchte und das alles mit Array durchführen will, wie müsste es denn dann gehen? Kann ich das dann überhaupt mit einer FUnktion machen?

    VIelen Dank
    lg, freakC++



  • freakC++ schrieb:

    Hallo,
    ok, vielen Dank! Das ist einleuchtend. Wenn ich nun ohne vector arbeiten möchte und das alles mit Array durchführen will, wie müsste es denn dann gehen? Kann ich das dann überhaupt mit einer FUnktion machen?

    VIelen Dank
    lg, freakC++

    Nenne einen guten Grund, warum du es nicht mit std::vector machen möchtest 😉

    Du müsstest in der Funktion mit new double[6] Speicher auf dem Heap allokieren und später auch wieder frei geben.

    Lars



  • Wieso verwendest du double und int ?
    Reste bei der Division können in deinem Fall ja jetzt gar nicht entstehen 😉



  • Weil mich auch immer die herkömmliche Variante interessiert. Deswegen möchte ich wissen, wie man das ohne std::vector machen kann.

    Die Werte sind nur Beispiel. In Wirklichkeit sind das Divisionen, die nicht aufgehen.

    Vielen Dank für eure Hilfe
    lg, freakC++



  • Vorweg: es ist dringend anzuraten, das mit vector oder boost::array oder etwas Vergleichbarem zu machen.

    Ansonsten gäbe es 2 Möglichkeiten:

    1. Array dynamisch anlegen

    double* foo(...)
    {
        double *Ri = new double[6];
        ...
        return Ri;
    }
    
    // Nachteil: Aufrufer muss Array freigeben
    double *arr = foo(...);
    ...
    delete[] arr; // !
    // ---> schlechter Stil, Speicheranforderung und -freigabe sind getrennt
    

    2. Schon angefordertes Array wird per Parameter übergeben:

    void foo(double* arr, ...)
    {
        arr[0] = ...;
        ...
    }
    
    // Aufruf
    double R[6];
    foo(R);
    // ---> schon besser, funktioniert aber nicht direkt, wenn Aufrufer Größe des Arrays nicht kennt
    

    Bei beiden Möglichkeiten machst du es dir aber unnötig schwer.



  • Hallo,
    ja, aber ich muss so langsam wieder in C++ reinkommen und mache mir darüber Gedanken. Ich habe über die Zeit viel vergessen und muss jetzt einiges rekapitulieren.

    Deswegen kommen solche Fragen jetzt bestimmt häufiger.

    Ihr habt zwar Recht, aber ich finde, man sollte beides könnnen.

    lg, freakC++



  • was ganz unflexibeles (und unsicheres) :p

    void calc_R(int (&RI)[6]) //arr hat 6 Elemente 
    { 
     for (int i=0; i<6; i++) 
       RI[i] /= SENSI; //SENSI ist eine Konstante (50) 
    
     return;
    } 
    
    int main() 
    { 
     static int num[6] = {100,150,200,250,300,350}; 
     calc_R(num); 
     for (int i=0; i<6; i++) 
       cout << num[i] << endl; 
    
     return 0; 
    }
    


  • Machen kannst du es z.B. so, wie oben von mir schon gepostet,

    oder so:

    #include <iostream>
    using namespace std;
    
    void calc_R(int* arr, double* Ri)
    {
        for (int i=0; i<6; i++)
            Ri[i] = arr[i] / 50.0;
    }
    
    int main()
    {
        const int SIZE = 6;
    
        int num[SIZE] = {100,150,200,250,300,350};
        double R[SIZE] = {};
    
        calc_R(num,R);
    
        for(int i=0; i<6; i++)
            cout << R[i] << endl;
    
        system("pause>nul");
        return 0;
    }
    

    oder so:

    #include <iostream>
    using namespace std;
    
    void calc_R(double* num)
    {
        for (int i=0; i<6; i++)
            num[i] = num[i] / 50.0;
    }
    
    int main()
    {
        double num[6] = {100,150,200,250,300,350};
    
        calc_R(num);
    
        for(int i=0; i<6; i++)
            cout << num[i] << endl;
    
        system("pause>nul");
        return 0;
    }
    


  • ipsec schrieb:

    Vorweg: es ist dringend anzuraten, das mit vector oder boost::array oder etwas Vergleichbarem zu machen.

    Aus reiner Neugier: Warum?
    Simon



  • Das frage ich mich auch.

    @oneill5000: Bei dir ist aber das Problem, dass mein num Array dann überschrieben wird, wenn ich mit Referenzen arbeite. Das möchte ich aber nicht.

    edit: Das gleiche gilt für das zweite Beispiel von Dweb. Du arbeitest zwar nicht mit Referenzen aber Du überschreibst es dennoch.

    edit2: Ich hab es zwar nicht ausprobiert, aber wird das Array im ersten Beispiel von Dweb nur lokal verändert?

    VIelen Dank
    lg, freakC++



  • theta schrieb:

    ipsec schrieb:

    Vorweg: es ist dringend anzuraten, das mit vector oder boost::array oder etwas Vergleichbarem zu machen.

    Aus reiner Neugier: Warum?
    Simon

    Einige wenige Punkt, die für vector und gegen eine händische Lösung sprechen:

    • (Exception-)Sicherheit
    • geringerer Aufwand
    • weniger Fehlerquellen
    • besseres Design
    • generell kein manuelles Speichermanagement notwendig (ist fast immer von Vorteil)
    • besser erweiter- und wartbar

    In puncto Performance ist eine vector -Rückgabe zwar schlechter bestellt (wenn die NRVO nicht greift), das ändert sich aber in C++0x mit Moving-Konstruktoren. Außerdem ist der Unterschied bei 6 Werten sowieso irrelevant.



  • freakC++ schrieb:

    edit: Das gleiche gilt für das zweite Beispiel von Dweb. Du arbeitest zwar nicht mit Referenzen aber Du überschreibst es dennoch.

    Dann kannst du mein erstes Beispiel verwenden oder ebn dynamische Speicherverwaltung, wie in meinem ersten Post

    freakC++ schrieb:

    edit2: Ich hab es zwar nicht ausprobiert, aber wird das Array im ersten Beispiel von Dweb nur lokal verändert?

    Nein, wenn man dir schon eine Lösung anbietet, dann kannst du sie doch auch einfach schnell testen, ob's deinen Erwartungen entspricht. 👍



  • Ich denke auch, dass man std::vector mehr nutzen sollte, doch wie gesagt: Man muss beides können.

    edit: Klar, Du hast recht, Dweb. Ich wollte dir nur sagen 😉 😉 😉

    lg, freakC++



  • Hallo,
    ich habe noch eine andere Frage. Diese passt noch gut zu dieser Threadüberschrift. Ich möchte ein zweidimensionales Array an eine Funktion übergeben, die dann zu jedem Element 2 addiert und das Array dann wieder zurückgibt - wieder eine Rückgabe von einem Array. Das müsste eigentlich genauso gehene wie mit 1D Arrays, doch ich meine, dass man Zeiger auf Zeiger braucht. Stimmt das:

    ICh dachte an folgendes zur Übergabe:

    double** addtwo (double** arr)
    {
     for(int i=0; i<6; i++)
       for(int j=0; j<6;j++)
        arr[i][j]+=2;
    //Rückgabe des Arrays
    
     return arr;
    }
    //Es kommt ein initialisertes [6][6] Array names foo.
    
    addtwo(foo); //Fehler: Konvertierung nicht möglich
    

    Wie funktioniert das denn bei zweidimensionalen Arrays. Liege ich mit meinen doppelten Zeigern falsch? WO liegt der Fehler bei der Übergabe?

    Bitte nicht meckern, dass ich es mit extra umständlich machen will 😃 😃

    Herzlichen Dank
    lg, freakC++



  • Die Lösung zu diesem Problem findet du hier: http://c-faq.com/aryptr/pass2dary.html . Da du offenbar versuchst Arrays und Pointer zu verstehen, wäre vermutlich das gesamte Kapitel 6 dieser FAQ eine schöne Lektüre.



  • Sagen wir so: Ich versuche sie wieder zu verstehen. Ich muss gründlich wiederholen. Die Logik hinter Zeigern (jedenfalls den einfachen ist mir klar). Ich lese mir das mal durch, wenn ich Zuhause bin. Jetzt fahre ich erstmal 45 min Auto und dann schau ich mal, ob jemand noch einen hilfreichen Post gemacht hat 😃 😃

    Ich danke euch!

    Bis dann
    lg, freakC++



  • Hallo zusammen,
    die 45 min zu rum 🙂 und ich habe mir die gepostete Seite durchgelesen und geschafft, was ich machen wollte: 2D Array als Parameter einer Funktion. Ich habe dennoch noch zwei Fragen. Mein Code sieht nun so aus:

    void foo(int (*ap)[6])	
    {
      for (int i=0; i < SIZE; i++)
        for(int j=0; j < SIZE; j++)
          cout << ap[i][j];
    }
    

    1.) Warum funktioniert diese Ausgabe? Woher weiß der Compiler, dass es sich um ein zweidimensionales Array handelt und was läuft intern ab?

    2.) Die eine Dimension ist beliebig groß, doch die zweite ist mit 6 festgelegt. Wenn diese nun auch beliebig groß sein soll bzw. unbekannt bei der Übergabe, wie muss die Funktion dann modifiziert werden? Ich dachte ursprünglich an folgendes, doch das ist anscheinend völliger Quatsch:

    void foo(int (*ap)*[]);
    

    Vielen Dank
    lg, freakC++



  • Die Antwort ist: Es gibt keine 2-Dimensionalen Arrays. Es gibt aber Arrays von Arrays.

    // bar ist ein 6-elementiges Array von 6-elementigen Arrays von int
    int bar[6][6];
    

    Übergibst Du das einer Funktion:

    void foo(int (*ptr)[6]);
    ...
    foo(bar);
    

    sieht das dann inklusive Zeiger ptr so aus im Speicher:

    ptr                    ptr+1         ptr+5
    
        |                       |             |
        |                       |             |
        V                       V             V
    
      bar[0]_______________   bar[1]_ ...   bar[5]_______________
     /                     \ /             /                     \
    +---+---+---+---+---+---+---+---+ ... +---+---+---+---+---+---+
    |   | * |   |   |   |   |   |   |     |   |   |   |   |   | * |
    +---+---+---+---+---+---+---+---+ ... +---+---+---+---+---+---+
          |                                                     |
      bar[0][1]                                             bar[5][5]
    

    ptr ist ein Zeiger auf ein 6-elementiges Array von ints. Beachte, dass ptr[2] dasselbe ist wie *(ptr+2) . Und da ptr vom Typ int(*)[6] ist, sollte auch klar sein, was bei der Pointer-Arithmetik passiert. ptr[2] ist also ein Lvalue-Ausdruck für ein 6-elementiges int-Array. Damit kann man zB auch wieder eine Referenz initialisieren:

    int (&arrayref)[6] = ptr[2];
    arrayref[3] = 9;
    assert(arrayref[3] == ptr[2][3]);
    

    Jetzt sollte auch klar sein, warum man die Größe der "inneren Arrays" festlegen muss. Alles in der Richtung int (*ptr)[] ist völliger Quatsch. Du kannst eben keine Pointer-Arithmetik durchführen, wenn Du nicht weißt, wie groß ein Objekt des Typs ist, auf das der Zeiger zeigt.

    kk


Anmelden zum Antworten