2D arrays effizient übergeben und zurückgeben?



  • Vielen Dank. Das ist interessant. eigen kannte ich noch nicht.

    Aber darf ich dennoch fragen, wie man das eigentliche Problem lösen könnte? Sonst lerne ich ja nicht. 😉



  • Ich würde einen Zeiger auf einen Zeiger übergeben.

    double g_matrix[X][X];
    int main()
    {
       foobar(g_matrix);
    }
    
    void foobar(double **matrix)
    {
    
    }
    


  • Autsch!

    Vielen Dank - ich sagte glaube ich schon, dass ich gerade auf dem Schlauch stehe, oder?

    Danke,
    Christian



  • CMB schrieb:

    Hierbei hätte matrix eine feste Größe. Habt ihr einen besseren Vorschlag? Wie gehe ich am besten bei variabler Größe vor? Welche keywords sollte ich ggf. berücksichtigen?

    Sofern du nicht auf eine spezielle Matrixbibliothek zurückgreifst wäre vielleicht std::vector etwas für dich (kann man auch verschachteln).

    CMB schrieb:

    Mich irritiert, dass ich double vor matrix setzen muß und sich sonst der Compiler beschwert:

    void foo(double matrix[3][3], ...) {
        double matrix[3][3] = {{ 1.0, 0.0, 0.0},
                               { 0.0, 1.0, 0.0},
                               { 0.0, 0.0, 1.0}};
    

    matrix ist dem Compiler doch bekannt, oder? Wieso also 'double matrix[][]?

    Edit: dank Hinweis von scorcher24.
    matrix ist innerhalb der Funktion schon bekannt, nur Initialisieren geht nur bei der Deklaration. Du schreibst hier auch garnicht in den Parameter matrix, sondern in eine neue, lokale Variable die den Parameter verdeckt.

    ...Auch die Ausmessungen des Arrays sind von Haus aus nicht bekannt (Es sei den, du verabschiedest dich von C-Arrays, und nimmst statt dessen C++ Klassen mit entsprechenden Funktionalitäten), wenn du mit Arrayübergaben arbeitest, musst du entweder sicherstellen das die Größe immer gleich bleibt, oder die Bereiche mit übergeben.

    Und bei Klassen: Effiziente Übergabe mittels Referenzen oder konstanten Referenten, nicht per Wert.

    cu André
    P.S: Und globale Variablen, wie auch von scorcher gezeigt sollte man möglichst meiden.



  • matrix ist dem Compiler nicht global bekannt.

    In dem Context war das auch innerhalb der Funktion foo gemeint, geh ich mal von aus. Wollte das auch erst schreiben, aber beim 2ten lesen merkt mans :).

    P.S: Und globale Variablen, wie auch von scorcher gezeigt sollte man möglichst meiden.

    Schnelles Beispiel und so...
    rya.



  • Scorcher24 schrieb:

    Ich würde einen Zeiger auf einen Zeiger übergeben.

    double g_matrix[X][X];
    int main()
    {
       foobar(g_matrix);
    }
    
    void foobar(double **matrix)
    {
    
    }
    
    error: argument of type "double (*)[5]" is incompatible with
              parameter of type "double **"
    

    Es gibt grundsätzlich zwei Ansätze

    1. Du machst es auf die harte Tour manuell mit Zeigern (so ähnlich, wie Scorcher vorgeschlagen hat) oder

    2. Du benutzt std::vector

    Zu beidem mal ein Beispiel:

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    void PrintMatrix(double **matrix, size_t height, size_t width)
    {
       for(size_t i = 0; i < height; ++i)
       {
           for(size_t j = 0; j < width; ++j)
           {
               cout << matrix[i][j] << " ";
           }
           cout << endl;
       }
    }
    
    //Per const-Referenz übergeben - verhindert aufwendiges Kopieren
    void PrintMatrix(vector<vector<double> > const &matrix)
    {
       //Ja, Iteratoren wären schöner gewesen
       for(size_t i = 0; i < matrix.size(); ++i)
       {
           for(size_t j = 0; j < matrix[i].size(); ++j)
           {
               cout << matrix[i][j] << " ";
           }
           cout << endl;
       }
    }
    
    int main()
    {
       {
          //1.1) Alternative - Array in ein Array aus Pointern umwandeln :(
          double array[][3] = {{1, 2, 3},
                               {4, 5, 6},
                               {7, 8, 9}};
          double *pointerArray[3] = {array[0], array[1], array[2]};
          PrintMatrix(pointerArray, 3, 3);
       }
       {
          //1.2) Array direkt mit dynamischer Speicherverwaltung als Array von Pointern basten - hat den Vorteil, dass es dynamischer ist
          size_t height, width;
          cin >> height >> width;
          double **array = new double*[height];
          for(size_t i = 0; i < height; ++i)
          {
             array[i] = new double[width];
          }
          //Initialisierung der Werte fehlt...
          PrintMatrix(array, height, width);
          for(size_t i = 0; i < height; ++i)
          {
             delete[] array[i];
          }
          delete[] array;
       }
       {
          //2. Alternative mit std::vector
          double array[][3] = {{1, 2, 3},
                               {4, 5, 6},
                               {7, 8, 9}};
          //Geht auch schöner, aber so wahrscheinlich einfacher und für mich schneller ;)
          vector<vector<double> > mat;
          for(size_t i = 0; i < 3; ++i)
          {
              mat.push_back(vector<double>(array[i] + 0, array[i]+3));
          }
          PrintMatrix(mat);
       }
    }
    

    Hoffe, das hilft

    Felix



  • Vielen vielen Dank.

    Erst war ich zögerlich <vector> zu nutzen, weil ich den Overhead klein halten wollte. Absolut lächerlich. Typerischer Fall von voreilieger & sinnloser Optimierung. Jetzt komme ich mit wesentlich weniger Code aus, der natürlich auch viel übersichtlicher und intuitiver zu nutzen ist.

    Aber es ist so lange her, dass ich C++ genutzt habe, dass ich mir diese Gehschwierigkeiten verzeihe.

    Gruß,
    Christian



  • CMB schrieb:

    Erst war ich zögerlich <vector> zu nutzen, weil ich den Overhead klein halten wollte.

    Es gibt zwar einen "Overhead" dieser ist jedoch gering. Die Standardbibliothek ist meist recht gut optimiert, zumindest besser, als wenn man es ohne großen Aufwand selbst schreiben würde.

    CMB schrieb:

    Jetzt komme ich mit wesentlich weniger Code aus, der natürlich auch viel übersichtlicher und intuitiver zu nutzen ist.

    Leider ist die Standardbibliothek nicht immer so intuitiv (gerade für Neulinge ist das Konzept nicht immer verständlich).

    cu André



  • Scorcher24 schrieb:

    Ich würde einen Zeiger auf einen Zeiger übergeben.

    double g_matrix[X][X];
    int main()
    {
       foobar(g_matrix);
    }
    
    void foobar(double **matrix)
    {
    
    }
    

    Hast du das überhaupt schon mal probiert? - Das geht nicht. Ich weiss gar nicht, warum sich das so verbreitet hat..
    Auch ein 2-Dimensionales Array ist lediglich 1-Dimensional. Wenn du das also per Zeiger machen willst, dann eher so:

    double g_matrix[X][X];
    int main()
    {
       foobar(&g_matrix[0][0], X,X);
    }
    
    void foobar(double* matrix, int dim1, int dim2)
    {
    
    }
    


  • Schon wieder so ein Fall, wo einfach mal std::vector empfohlen wird.

    Hier wäre meiner Ansicht ein std::tr1::array viel geeigneter. Erstens spart man sich den Overhead, da man direkt auf einem statischen Array arbeitet, und zweitens braucht man die Funktionalität des std::vector s (dynamische Verwaltung) hier gar nicht.

    void PrintMatrix(const std::tr1::array<std::tr1::array<double, 3>, 3>& Matrix);
    

    Oder ohne Namensraum-Kennzeichner, um ein gleichwertiges Beispiel zu liefern:

    void PrintMatrix(const array<array<double, 3>, 3>& Matrix);
    

    Geht natürlich nur mit TR1-Unterstützung. Der Header dazu ist

    #include <array>
    

    Klar, wenn man die Matrixgrösse nicht weiss oder irgendwelche andere Funktionalität des std::vector s braucht, ist der schon okay. Aber ich wollte dem Threadersteller noch eine andere, leider nicht so verbreitete Möglichkeit zeigen.


Anmelden zum Antworten