Speicher fuer dim x dim Matrix anfordern


  • Mod

    Unnötige, schlechte Eigennachbauten von Standardklassen? Ich höre den Ruf und eile herbei!

    template<typename ElementType> class MyUnnecessaryAndTediousAndBadCloneOfStdVectorThatOnlyWorksForDefaultConstructibleTypesBecauseIAmLazy
    {
    private:
      ElementType *data;  
      unsigned long size;
    public:
      MyUnnecessaryAndTediousAndBadCloneOfStdVectorThatOnlyWorksForDefaultConstructibleTypesBecauseIAmLazy(unsigned long size): data(new ElementType[size]), size(size) { }
      ~MyUnnecessaryAndTediousAndBadCloneOfStdVectorThatOnlyWorksForDefaultConstructibleTypesBecauseIAmLazy() { delete[] data; }
      MyUnnecessaryAndTediousAndBadCloneOfStdVectorThatOnlyWorksForDefaultConstructibleTypesBecauseIAmLazy
      (const MyUnnecessaryAndTediousAndBadCloneOfStdVectorThatOnlyWorksForDefaultConstructibleTypesBecauseIAmLazy& other):
        data(new ElementType[other.size]), size(other.size)
      {
        // My unnecessary, bad and tedious clone of std::copy
        // which is not even the correct way of doing this 
        // (should get uninitialized memory and use something 
        // like uninitalized_copy)
        for (unsigned long i = 0; i < size; ++i)
          data[i] = other.data[i];
      }
      MyUnnecessaryAndTediousAndBadCloneOfStdVectorThatOnlyWorksForDefaultConstructibleTypesBecauseIAmLazy& operator=
    (MyUnnecessaryAndTediousAndBadCloneOfStdVectorThatOnlyWorksForDefaultConstructibleTypesBecauseIAmLazy other)
      {
        // My unnecessary, bad and tedious clone of std::swap
        ElementType* temp = data;
        data = other.data;
        other.data = temp;
        unsigned long temp2 = size;
        size = other.size;
        other.size = temp2;
    
        return *this;
      }
    
      ElementType& operator[](unsigned long i) { return data[i]; }
      const ElementType& operator[] (unsigned long i) const { return data[i]; }
    };
    
    template <typename ElementType> class Array2D
    {
    private:
      MyUnnecessaryAndTediousAndBadCloneOfStdVectorThatOnlyWorksForDefaultConstructibleTypesBecauseIAmLazy<ElementType> data;
      unsigned long size_x;
    
     public:
      Array2D(unsigned long size_x, unsigned long size_y) : data(size_x * size_y), size_x(size_x) {}
      ElementType& operator()(unsigned long x, unsigned long y) { return data[x + y * size_x]; }
      const ElementType& operator() (unsigned long x, unsigned long y) const { return data[x + y * size_x]; }  
    };
    
    #include <iostream>
    int main()
    {
      using std::cout;
      Array2D<int> foo(4,5);
    
      int i = 0;
      for (int x = 0; x < 4; ++x)
        for (int y = 0; y < 5; ++y)
          foo(x,y) = ++i;
    
      // Ein bisschen RAII testen:
      Array2D<int> bar(foo);
      Array2D<int> foobar(7,3);
      foobar = bar;
    
      for (int x = 0; x < 4; ++x)
        {
          for (int y = 0; y < 5; ++y)
            cout << foobar(x,y) << " ";
          cout << '\n';
        }
    }
    

    cout habe ich jetzt mal nicht nachgebaut, weil das irgendwann auf systemabhängiger Ebene geendet hätte.



  • Du hast operator new nicht redefiniert.
    Klares Fail.
    🤡



  • Tyle schrieb:

    int groesse = 42;
    int *pArray;
    pArray = new int[groesse];
    
    delete [] pArray;
    

    Und gespickt habe ich da nicht. Das Problem ist das new und delet bei uns auch nur in ca. 10 Zeilen Code erklärt wird.

    Das sieht doch gut aus.

    Wieso lässt Du Dich dann so verwirren, dass Du den ersten Code schreibst?

    Was Du jetzt hast ist ein Array von 42 ints.

    Sagen wir ich wollte davon fünfzigtausend Stück haben.
    Ergo brauche ich fünfzigtausend Zeiger wie pArray , die jeweils auf 42 ints zeigen.
    Da pArray ein int* ("Zeiger auf int") ist hat ein Zeiger auf ein solches Array den Typ int** ("Zeiger auf Zeiger auf int").
    In einem ersten Schritt alloziere ich die 50k Zeiger-auf-Zeiger und in einem weiteren für jeden von diesen 42 ints.

    int main(){
      const int dim1=50000, dim0=42;
      int** ppArray = new int*[dim1]; // Schritt 1: Speicher fuer dim1 mal int*
      for(int i=0; i<dim1; ++i)       // Schritt 2: dim1 mal Speicher fuer dim0 ints
        ppArray[i]=new int[dim0];
      //....
      for(int i=0; i<dim1; ++i)
        delete[] ppArray[i];
      delete[] ppArray;
    }
    

    Beachte, dass hinter new ein Typ steht: new int*[size]; bedeutet ein Array von int*.
    new double**[size]; ist dementsprechend ein Array von double**
    Deine fehlerhafte Syntax sah so aus, als ob Du irgendwas mit einer Größe multiplizieren wolltest um den Speicherbedarf anzugeben.


  • Mod

    SeppJ schrieb:

    Warum nicht x*y dim0*dim1 an einem Stück?



  • SeppJ schrieb:

    SeppJ schrieb:

    Warum nicht x*y dim0*dim1 an einem Stück?

    Kannst Du dafür nicht einen eigenen Thread aufmachen, wenn Du das diskutieren willst?
    Ich schätze dem OP wäre damit nicht bei seinem akuten Problem geholfen.

    Aber schreib doch die paar Zeilen. Besser als zweimal den gleichen Halbsatz zu posten wird's allemal.

    Go for it!


  • Mod

    Habe ich oben ja schon. Ob man da nun MyUnnecessaryAndTediousAndBadCloneOfStdVectorThatOnlyWorksForDefaultConstructibleTypesBecauseIAmLazy oder einen anderen Container einsetzt ist bewusst anpassbar gelassen.

    Es ist aber halt ziemliche Verschwendung, das nicht alles an einem Stück anzufordern. Das würde man niemals so machen, weil es lauter dicke Nachteile hat und man hier keinen der Vorteile benutzt.



  • Wieso lässt Du Dich dann so verwirren, dass Du den ersten Code schreibst?

    Das war vorher ein C-Programm mit malloc und free. Darin habe ich dann stümperhaft rumgewerkelt.

    @SeppJ Kannst ja mal ne Beispiel für ne Code am Stück liefern.



  • Ok ich glaube ich habe es nun. Scheint auch soweit zu funktionieren bekomme nur eine Warnug zu der time_t Funktion.

    Warnung:
    quelle.cpp(35): warning C4244: 'Argument': Konvertierung von 'time_t' in 'unsigned int', möglicher Datenverlust

    #include <cstdlib> 
    #include <ctime>
    #include <iostream>
    #include <conio.h>
    
    using namespace std;
    
    int main() {
      double **matrix; //Zeiger auf Zeiger auf Matrix
      int dim;
    
      cout << "Dimension eingeben: ";
      cin >> dim;
    
      //Speicher fuer dim x dim  Matrix anfordern
      matrix = new double*[dim];		//Speicher fuer dim mal int*
      for(int i=0; i<dim; ++i)			//dim1 mal Speicher fuer dim ints
        matrix[i]=new double[dim];
    
      //Mit Zufallswerten fuellen
      srand(time(NULL));
      for(int i=0 ; i<dim ; i++) 
        for(int j=0 ; j<dim ; j++) 
          matrix[i][j] = (double)rand(); 
    
      //Ausgeben
      cout << "Erzeugte Matrix: " << endl;
      for(int i=0 ; i<dim ; i++) {
        for(int j=0 ; j<dim ; j++) { 
          cout << matrix[i][j] << " ";
        }
        cout << endl;
      }
    
      //Speicher wieder freigeben
       for(int i=0; i<dim; ++i)
        delete[] matrix[i];
      delete[] matrix;
    
      _getch();
      return 0;
    }
    

    Jetzt kann ich mich an was komplizierteres wagen um zu sehen ob ich das verstanden habe. Danke nochmals für eure Hilfe.


  • Mod

    Tyle schrieb:

    @SeppJ Kannst ja mal ne Beispiel für ne Code am Stück liefern.

    SeppJ schrieb:

    Habe ich oben ja schon. Ob man da nun MyUnnecessaryAndTediousAndBadCloneOfStdVectorThatOnlyWorksForDefaultConstructibleTypesBecauseIAmLazy oder einen anderen Container einsetzt ist bewusst anpassbar gelassen.

    SeppJ schrieb:

    template <typename ElementType> class Array2D
    {
    private:
      HierirgendetwasdasElementTypespeichernkannzurnotaucheinenpointermitnew data;
      unsigned long size_x;
     
     public:
      Array2D(unsigned long size_x, unsigned long size_y) : data(size_x * size_y), size_x(size_x) {}
      ElementType& operator()(unsigned long x, unsigned long y) { return data[x + y * size_x]; }
      const ElementType& operator() (unsigned long x, unsigned long y) const { return data[x + y * size_x]; }  
    };
    


  • Ok ich bin schon ein ganzes Stück weitergekommen aber es werden nach beenden der .exe noch Fehler angezeigt, bzw. da Programm stürzt dann ab.

    Auch kann ich keine Dimension größer als 9 benutzen, warum weiss ich nicht.

    Code ist noch ein wenig holprig, aber bin ja noch am lernen 🙄

    Hier mal das Programm:

    #include <iostream>
    #include <iomanip>
    #include <cmath>
    #include <conio.h>
    #define M_PI 3.14159265358979323846	//M_PI wirk komischerweise vom Compiler nicht definiert
    
    using namespace std;
    
    // Template Prototypefunktion //
    template <typename T> T dot(T *x, T *y, int n);
    
    int main() {
      //Vektoren
      int *xi, *yi;
      float *xf, *yf;
      double *xd, *yd;
      bool *xb, *yb;
    
      //Matrizen
      int **I;
      float **F;
      double **D1;
      double **D2;
    
      int n;
      cout << "Bitte Dimension n eingeben: ";
      cin >> n;
      //const float M_PI = 3.141;
    
      //Speicher reservieren
      D1 = new double *[n];
      for(int i=0; i<n; ++i)
    		D1[i]=new double[n];
    
      D2 = new double *[n];
      for(int i=0; i<n; ++i)
    		D2[i]=new double[n];
    
      I = new int *[n];
      for(int i=0; i<n; ++i)
    		I[i]=new int[n];
    
      F = new float *[n];
      for(int i=0; i<n; ++i)
    		F[i]=new float[n];
    
      xi = new int;
      yi = new int;
      xf = new float;
      yf = new float;
      xd = new double;
      yd = new double;
      xb = new bool;
      yb = new bool;
    
      //Vektoren und Matrizen mit Werten fuellen  
      for(int i=0 ; i<n ; i++) {
        xi[i] = i+1;    yi[i] = i-1;
        xf[i] = 0.1f*i; yf[i] = 0.25f*i;
        xd[i] = M_PI*i; yd[i] = 1e-10*i;
        xb[i] = (i%2 == 0); yb[i] = (i%3 == 0);
    
        for(int j=0 ; j<n ; j++) {
          if(i==j) I[i][j] = 1; else I[i][j] = 0;
          if(i>j)  F[i][j] = 0.2*i; else F[i][j] = 0.2*j;
          D1[i][j] = M_PI*(i+j);
        }
    
      }
    
      cout << setprecision(2);
      cout << "Skalarprodukt von int Vektoren: "    << dot(xi,yi,n) << endl;
      cout << "Skalarprodukt von float Vektoren: "  << dot(xf,yf,n) << endl;
      cout << "Skalarprodukt von double Vektoren: " << dot(xd,yd,n) << endl;
    
      //Speicher wieder freigeben
      for(int i=0; i<n; ++i)
        delete[] D1[i];
      delete[] D1;
    
      for(int i=0; i<n; ++i)
        delete[] D2[i];
      delete[] D2;
    
      for(int i=0; i<n; ++i)
    	delete[] I[i];
      delete[] I;
    
      for(int i=0; i<n; ++i)
    	delete[] F[i];
      delete[] F;
    
      delete xi;
      delete yi;
      delete xf;
      delete yf;
      delete xd;
      delete yd;
      delete xb;
      delete yb;
    
      _getch();
    
      return 0;
    
    }
    
    // Template Funktion Definition //
    template <typename T> T dot(T *x, T *y, int n) {
    	T res = 0;
    	for (int i=0; i<n; i++)
    		res += x[i]*y[i];
    	return  res;
    }
    

  • Mod

    Erklär mal, was du dir bei Zeile 47 ff. gedacht hast.



  • Nun ja ich habe am anfang nur die Matritzen mit new belegt und der Kompiler hat immer die Variablen angemeckert.

    Habe daher ne bissel mit trail-and-error rumexperimentiert.

    Nachdem ich Speicher für die Variablen allokiert habe hat das Programm soweit (bis das Ende) funktioniert.


  • Mod

    Tyle schrieb:

    Habe daher ne bissel mit trail-and-error rumexperimentiert.

    Uralter, aber absolut wahrer Spruch hier aus dem Forum:

    c.rackwitz schrieb:

    Wenn du selber Code schreibst, musst du ihn auch verstehen. Code ist kein Haufen von wahllos zusammengeschmissenen Buchstaben und Zeichen, Code ist Logik pur. Du musst genau wissen, warum du wo und welches Zeichen setzt.

    Studiere diese Aussage genau und lerne daraus.



  • Mit ist schon klar das ich das Programm noch nicht verstanden habe, daher versuche ich das ganze ja zu verstehen.

    Meine Materialien sind leider in Bezug auf new und delet sehr sehr dürftig. Muss daher leider viel ausprobieren.

    Wäre super wenn mir jemand erklären könnte was ich genau falsch gemacht habe und die Logik dahinter klar macht.



  • Tyle schrieb:

    Meine Materialien sind leider in Bezug auf new und delet sehr sehr dürftig. Muss daher leider viel ausprobieren.

    new und delete sind ja auch keine Raketentechnik, da muss man gar nicht soviel schreiben.
    Subtil sind allerdings die Anforderungen, die man erfüllen muss, damit man keine Speicherlecks hat. Aber auch dafür haben sich mittlerweile Lösungen gefunden - meistens in der Form, dass man kein new und delete benutzt.
    State of the Art sind dagegen Handles: std::string , std::vector usw. usf..

    Auch auf die Gefahr hin mich zu wiederholen:
    new gibt einen Zeiger auf allozierten Speicher zurück.
    Der zurückgebene Zeiger hat einen Typ abhängig vom allozierten Objekt.

    Für eine einzige Instanz ist die Form
    auto a = new int; // a ist int*
    für ein Array ist die Form:
    auto aa = new double[12]; // aa ist double*, aa zeigt auf's erste Element

    So allozierten Speicher gibt man nach Gebrauch mit delete wieder frei.
    delete a; // a von oben
    , bzw.
    delete[] aa; // aa von oben

    So und jetzt kommst Du und erklärst uns: was Du Dir bei Zeile 47ff gedacht hast.
    Bzw. was Du jetzt darüber denkst.
    Zur Erinnerung: Du weisst, wie Du Arrays (aka. Vektoren) mit new anlegst.



  • Also ich glaube ich habe den Fehler gefunden:

    Vorher habe ich mit

    xi = new int;
    

    nur Speicher für einen Int Wert reserviert. Was natürlich totaler Quatsch ist, das xi ein Zeiger vom Typ int ist und somit ein Array da ein Array ja auch nur ein Zeiger auf das erste Element des Arrays ist.

    An der Stelle hätte ich es eigentlich schon sehen sollen, das die Werte als Array befüllt werden:

    xi[i] = i+1;    yi[i] = i-1;
        xf[i] = 0.1f*i; yf[i] = 0.25f*i;
        xd[i] = M_PI*i; yd[i] = 1e-10*i;
        xb[i] = (i%2 == 0); yb[i] = (i%3 == 0);
    

    Klar das meine vorherige Zuweisung das Programm zum Absturz gebracht hat, da ja kein Speicher, bzw. viel zu wenig Speicher zugewiesen wurde.

    Somit andert sich das Programm wie folgt:

    #include <iostream>
    #include <iomanip>
    #include <cmath>
    #include <conio.h>
    #define M_PI 3.14159265358979323846	
    
    using namespace std;
    
    // Template Funktion Prototype //
    template <typename T> T dot(T *x, T *y, int n);
    
    int main() {
      //Vektoren
      int *xi, *yi;
      float *xf, *yf;
      double *xd, *yd;
      bool *xb, *yb;
    
      //Matrizen
      int **I;
      float **F;
      double **D1;
      double **D2;
    
      //Teilaufgabe a)
      int n;
      cout << "Bitte Dimension n eingeben: ";
      cin >> n;
      //const float M_PI = 3.141;
    
      //Speicher reservieren
      D1 = new double *[n];
      for(int i=0; i<n; ++i)
    		D1[i]=new double[n];
    
      D2 = new double *[n];
      for(int i=0; i<n; ++i)
    		D2[i]=new double[n];
    
      I = new int *[n];
      for(int i=0; i<n; ++i)
    		I[i]=new int[n];
    
      F = new float *[n];
      for(int i=0; i<n; ++i)
    		F[i]=new float[n];
    
      xi = new int[n];
      yi = new int[n];
      xf = new float[n];
      yf = new float[n];
      xd = new double[n];
      yd = new double[n];
      xb = new bool[n];
      yb = new bool[n];
    
      //Vektoren und Matrizen mit Werten fuellen  
      for(int i=0 ; i<n ; i++) {
        xi[i] = i+1;    yi[i] = i-1;
        xf[i] = 0.1f*i; yf[i] = 0.25f*i;
        xd[i] = M_PI*i; yd[i] = 1e-10*i;
        xb[i] = (i%2 == 0); yb[i] = (i%3 == 0);
    
        for(int j=0 ; j<n ; j++) {
          if(i==j) I[i][j] = 1; else I[i][j] = 0;
          if(i>j)  F[i][j] = 0.2*i; else F[i][j] = 0.2*j;
          D1[i][j] = M_PI*(i+j);
        }
    
      }
    
      cout << setprecision(2);
    
      cout << "Skalarprodukt von int Vektoren: "    << dot(xi,yi,n) << endl;
      cout << "Skalarprodukt von float Vektoren: "  << dot(xf,yf,n) << endl;
      cout << "Skalarprodukt von double Vektoren: " << dot(xd,yd,n) << endl;
    
      //Speicher wieder freigeben
      for(int i=0; i<n; ++i)
        delete[] D1[i];
      delete[] D1;
    
      for(int i=0; i<n; ++i)
        delete[] D2[i];
      delete[] D2;
    
      for(int i=0; i<n; ++i)
    	delete[] I[i];
      delete[] I;
    
      for(int i=0; i<n; ++i)
    	delete[] F[i];
      delete[] F;
    
      delete [] xi;
      delete [] yi;
      delete [] xf;
      delete [] yf;
      delete [] xd;
      delete [] yd;
      delete [] xb;
      delete [] yb;
    
      _getch();
    
      return 0;
    
    }
    
    // Template Funktion Definition //
    template <typename T> T dot(T *x, T *y, int n) {
    	T res = 0;
    	for (int i=0; i<n; i++)
    		res += x[i]*y[i];
    	return  res;
    }
    

    Sehe ich das so richtig?



  • Ja, aber ein Array ist kein Zeiger!
    Ein Array kann in einen Zeiger auf des erste Element konvertiert werden, aber es ist kein Zeiger!
    Und was spricht gegen std::vector?
    Denn überlege mal, was passiert, wenn eine Speicheranforderung fehlschlägt...



  • Nathan schrieb:

    Und was spricht gegen std::vector?

    darf er nicht benutzen, hatte ich auf der ersten Seite schon gefragt.



  • Ok dann habe ich das mit dem Array falsch verstanden.

    Ist schon klar was passiert wenn die Speicheranforderung fehlschlägt, das Programm stürzt ab. Ich persönlich würde auch eher ein vector nutzen. Darf ich aber in dem Fall nicht machen da es in der Aufgabe mit new und delet gefordert wurde.

    Verbraucht denn der Vector nicht auch mehr Speicherplatz?



  • Nathan schrieb:

    Denn überlege mal, was passiert, wenn eine Speicheranforderung fehlschlägt

    Wenn eine Speicheranforderung fehlschlägt, dann ist eh schon alles zu spät. Dann hast du vorher schon Misst gebaut, sonst würde sie nicht fehlschlagen. Darum ist ein

    if( !Speicher_erfolgreich_allkoiert )
    

    sinnlos. Du musst hergehen und schauen, wie es zu solch einen Missgeschick kommen konnte (z.B. wenn du voher bereits 1000x new[] gemacht hast) und dann den Auslöser beseitigen.


Anmelden zum Antworten