Speicher fuer dim x dim Matrix anfordern


  • 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.



  • Tyle schrieb:

    Also ich glaube ich habe den Fehler gefunden
    [...]

    Sieht doch gut aus.

    Du hast jetzt tausend Möglichkeiten weiterzumachen.
    Kommt auf Deine Vorkenntnisse und Deinen Spaß an der Sache an.
    Mir fällt ein:

    • freie Funktionen zum erstellen, frei- und ausgeben der Matrix
    • eine Klasse, die die Matrix beinhaltet
    • Das ganze dann noch mit Memberfunktionen
    • Das array in einem Stück erstellen ( new double[dim*dim]; ) und schauen, ob Du die Zeigerarithmetik für die Zugriffe hingebogen bekommmst
    • tatsächlich die STL (namentlich std::vector und std::inner_product ) benutzen
    • Bierchen trinken gehen, weil Die Übung erledigt ist


  • Tyle schrieb:

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

    Falsche Schlussfolgerung.

    Du würdest mehr lernen, wenn du dich zu diesen Themen in einem guten C++-Buch einlesen würdest. In C++ kann "ausprobieren" fürchterlich nach hinten los gehen. Besonders wenn es sich um Dinge handelt, die zu funktionieren scheinen, aber tatsächlich undefiniertes Verhalten sind...


  • Mod

    out schrieb:

    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.

    Der Trick ist, so zu programmieren, dass dadurch kein Schaden entstehen kann. Wenn die bad_alloc fliegt (und nein: new gibt bei Fehlschlag keine 0 zurück, außer man schaltet dies explizit ein), dann möchte man doch wenigstens, dass alle Dateien ordentlich geschlossen, aller Sockets ungebunden, aller Speicher sauber freigegeben und die Datenbank in einen konsistenten Zustand gebracht wird. Kurz: Man möchte einen RAII-Handler für alles benutzen. Selbst wenn die Standardbibliothek verboten ist: So ein unique_ptr ist in wenigen Minuten selbst programmiert, wenn man sich auf die nötigsten Features beschränkt.

    Dabei würde man wirklich etwas über C++ lernen und nebenbei noch über new/delete. Im Gegensatz zu dieser Übungsaufgabe, die einen bloß lehrt, wie man es nicht täte.



  • Habe hier nur das Buch: "C++ Objektorientiertes Programiern von Anfang an" von Helmut Erlenkötter.

    Ansonsten schaue ich bei WikiBooks C++ nach.

    @Furble Wurble: Bin mit dem Programm auch noch nicht ganz am Ende, ich muss noch ne paar Template Funktionen einfügen und gucken ob die auch das machen was sie sollen.

    @SeppJ: Die Übungsaufgaben halte ich auch sehr praxis fern. Da wird einem Zeug beigebracht, welches man später niemals nutzen würde.



  • So mein Programm ist nur fertig:

    #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);
    template <> bool dot(bool *x, bool *y, int n);
    template <typename S, typename T> S matvec(S **x, T *y, S *z, int n);
    template <typename S, typename T> S matmat(S **x, T **y, S **z, int n);
    template <typename T> void out(T *x, int n);
    template <typename T> void out(T **x, int n);
    
    /*
    int dot(int *x, int *y, int n) {
      int res = 0;
      for(int i=0 ; i<n ; i++)
        res += x[i]*y[i];
      return res;
    } 
    
    float dot(float *x, float *y, int n) {
      float res = 0;
      for(int i=0 ; i<n ; i++)
        res += x[i]*y[i];
      return res;
    } 
    
    double dot(double *x, double *y, int n) {
      double res = 0;
      for(int i=0 ; i<n ; i++)
        res += x[i]*y[i];
      return res;
    } 
    */
    
    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;
    
      //Teilaufgabe b)
      cout << endl;
      cout << "Teilaufgabe b)" << endl;
      cout << "Skalarprodukt von bool Vektoren: "   << dot(xb,yb,n) << endl << endl;
    
      //Teilaufgabe c)
    
      cout <<endl <<"Teilaufgabe c)" << endl;
      matvec(D1,xf,xd,n);
      cout << "Matrix-Vektor-Produkt mit double und float: " << endl;
      out(xd,n);
      matvec(F,xi,xf,n);
      cout << "Matrix-Vektor-Produkt mit float und int: " << endl;
      out(xf,n);
    
      //Teilaufgabe d)
      cout <<endl << "Teilaufgabe d)" << endl;
      matmat(D1,I,D2,n);
      cout << "Matrix-Matrix-Produkt mit double und int: " << endl;
      out(D2,n);
      matmat(D1,F,D2,n);
      cout << "Matrix-Matrix-Produkt mit double und float: " << endl;
      out(D2,n);
    
      //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 Funktions Definition dot //
    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;
    }
    
    // Template Funktions Definition matvec //
    template <typename S, typename T> S matvec(S **x, T *y, S *z, int n) {
    	for (int i=0; i<n; i++){
             z[i]=0;
        }
    
        for (int i=0; i<n; i++){
            for (int j=0; j<n; j++){
                z[i]+=( x[i][j] * y[j]);
            }
        }
    		return *z;
    }
    
    // Template Funktions Definition matmat //
    template <typename S, typename T> S matmat(S **x, T **y, S **z, int n) {
    	for (int i=0; i<n; i++){
    		for(int j=0; j<n; j++) {
    			z[i][j]=0;
    		}
        }
    
        for (int i=0; i<n; i++){
            for (int j=0; j<n; j++){
    			for (int k=0; k<n; k++) {
    				z[i][j]+=( x[i][k] * y[k][j]);
    			}
            }
        }
    		return **z;
    }
    
    // Template Funktions Definition out //
    template <typename T> void out(T *x, int n) {
    	for(int i=0; i<n; i++) {
    		cout <<"Vektor: " << x[i] << endl;
    	}
    	cout << "Dimension: " << n << endl <<endl;
    }
    
    template <typename T> void out(T **x, int n) {
    	for(int i=0; i<n; i++) {
    		cout << endl;
    		for(int j=0; j<n; j++){
    			cout << setprecision(2) << setw(4) << x[i][j];
    		}
    	}
    	cout  << endl <<endl;
    }
    
    // Template Spezialisierung //
    template <> bool dot(bool *x, bool *y, int n) {
    	if (x == y)
    		return true;
    	else
    		return false;
    }
    


  • Deklarier doch die Variablen erst, wenn du sie initialisierst. Braucht weniger Code, ist übersichtlicher, und verhindert Fehler durch zu frühe Benutzung.

    if (x == y) 
             return true; 
         else 
             return false;
    

    kannst du einfacher schreiben als

    return x == y;
    

    Aber ich glaube, dieser Code ist nicht richtig, da du nur die Zeiger vergleichst und nicht die Inhalte des Arrays.

    Funktionstemplates solltest du nicht spezialisieren, sondern überladen.

    Und die Templatedefinitionen müssten streng genommen vor main() stehen, da der Compiler zur Zeit der Instantiierung die volle Definition kennen muss. Manche Compiler sind hier zwar nachlässiger, aber das macht es nicht richtig.


  • Mod

    Nexus schrieb:

    Und die Templatedefinitionen müssten streng genommen vor main() stehen,

    Eine solche Regel ist mir nicht bekannt (für Funktionstemplates).



  • Ah okay, ich dachte bisher, das wäre eine Eigenheit von MSVC. Das heisst, es reicht, wenn die Templatedefinition in der selben Übersetzungseinheit wie die Instanziierung steht?



  • @Tyle:
    Definiere doch mal _USE_MATH_DEFINES vor dem cmath Include. Dann dürfte auch PI definiert sein.

    Darfst du denn wenigstens Klassen nutzen? Dein Code ist ja an Grausamkeit schwer zu überbieten.


Anmelden zum Antworten