Probleme mit dynamischen Arrays (malloc und free)



  • Du solltest Dir das von SeppJ geschriebene zu Herzen nehmen... Du willst doch sauber und solide programmieren lernen und nicht irgendwas zusammenpfuschen.

    Um zu deinem Code zu kommen:

    values[r] = (int *)malloc(c * sizeof(int *));
    

    In dieser Zeile sind 4 grobe Fehler auf einmal.

    • Verwendung von malloc in C++
    • Verwendung von sizeof(int*), obwohl Du vermutl. sizeof(int) meinst
    • Verwendung von r als Index anstatt i --> off-by-one (indices gehen on 0-(r-1))
    • Verwendung von r als Index anstatt i --> Du überschreibst (r-1) allokierte Speicherbereiche und damit ist es ein Leak (weil Du den Speicher nie wieder freigeben kannst)
    • (int*) ist ein C-style cast und sollte in C++ ebenfalls nicht verwendet werden.
    for (int i = 0; i < c; i++)
    

    Das stimmt auch nicht, Du hast ja r Zeilen und nicht c Zeilen. Und da values[0] nicht initialisiert ist (allocate beschreibt nur das Element nach dem letzten Element in values), ist ein free auch nicht gültig, und damit eine Zugriffsverletzung.



  • @Rav1642
    ja das ist richtig. int **k ein zeiger auf was für einen datentyp? wenn int **k nach dem aufruf von malloc auf einen speicherbereich von n dieser elemente zeigen soll, welche speichermenge muss dann angefordert werden?



  • @HarteWare
    Danke für deine Antwort!

    Sehr gerne nehme ich mir das von ihm Geschriebene zum Herzen.
    Das habe ich ja bereits geschrieben, jedoch haben sich für mich Fragen aufgeworfen. Die Trennung von Funktionalität wie es SeppJ schrieb ist für mich nach wie vor nicht klar, weswegen ich ja nochmal nachgefragt habe.

    Zu Zeile :

    values[r] = (int *)malloc(c * sizeof(int *));
    

    @HarteWare sagte in Probleme mit dynamischen Arrays (malloc und free):

    • Verwendung von malloc in C++
    • Verwendung von sizeof(int*), obwohl Du vermutl. sizeof(int) meinst
    • Verwendung von r als Index anstatt i --> off-by-one (indices gehen on 0-(r-1))
    • Verwendung von r als Index anstatt i --> Du überschreibst (r-1) allokierte Speicherbereiche und damit ist es ein Leak (weil Du den Speicher nie wieder freigeben kannst)
    • (int*) ist ein C-style cast und sollte in C++ ebenfalls nicht verwendet werden.
    • dass ich malloc in C++ verwendet habe, war sicherlich kein guter Stil, das habe ich nun gelernt
    • das mit sizeof() war mir komplett entgangen, danke
    • mein ursprünglicher Fehler beruhte hierauf, das habe ich dann gelöst
    • das gilt auch hierfür
    • den Punkt verstehe ich nicht; ist das problematisch, weil es ein Pointer ist? Muss man gar keinen Pointer casten und kann ich das ersatzlos weglassen?

    Zu:

    for (int i = 0; i < c; i++)
    

    @HarteWare sagte in Probleme mit dynamischen Arrays (malloc und free):

    Das stimmt auch nicht, Du hast ja r Zeilen und nicht c Zeilen. Und da values[0] nicht initialisiert ist (allocate beschreibt nur das Element nach dem letzten Element in values), ist ein free auch nicht gültig, und damit eine Zugriffsverletzung.

    Das war mir dann auch aufgefallen. Aber danke, dass du mich darauf nochmal hingewiesen hast! 🙂

    @Wade1234
    Danke, dass du die Geduld aufbringst, denn jetzt glaub ich, dass ich dir folgen kann. Der entscheidende Tipp kam ja bei HarteWare zur Sprache.
    Es muss wahrscheinlich heißen:

    k = malloc(2 * sizeof(int *));
    

    Richtig?



  • @Rav1642 sagte in Probleme mit dynamischen Arrays (malloc und free):

    Punkt drei verstehe ich nicht. Natürlich existiert in der Mathematik eine Trennung.
    Allerdings ...

    Das ist nicht das, worauf SeppJ hinaus wollte. Er hat ja auch explizit von "Speicherverwaltung" und "Matrixrechenoperationen" geschrieben.
    Zur Speicherverwaltung gehört ja ein bisschen was dazu. Wenn du das manuell machst und das korrekt machen wolltest, noch mehr als das was du bisher gemacht hast. Das hat jetzt aber alles nichts mit der Klasse Matrix zu tun. Eine andere Klasse, die etwas ganz anderes macht, könnte evtl. genau die gleiche Logik zur Speicherverwaltung brauchen. Deswegen macht es keinen Sinn, so etwas kompliziertes und allgemeines, in einer konkreten Klasse wie Matrix zu implementieren.
    Und das nächste ist, vielleicht willst du das ja später anders machen. Wenn deine Matrix intern malloc oder new[] macht, hast du keine Chance, das von außen zu ändern. Stell dir aber vor, dass du mal einen Pool Allokator verwenden willst...



  • @Rav1642 sagte in Probleme mit dynamischen Arrays (malloc und free):

    @HarteWare
    Danke für deine Antwort!

    Sehr gerne nehme ich mir das von ihm Geschriebene zum Herzen.
    Das habe ich ja bereits geschrieben, jedoch haben sich für mich Fragen aufgeworfen. Die Trennung von Funktionalität wie es SeppJ schrieb ist für mich nach wie vor nicht klar, weswegen ich ja nochmal nachgefragt habe.

    Zu Zeile :

    values[r] = (int *)malloc(c * sizeof(int *));
    

    Es kommt sehr selten vor, dass man in Klassen doch einmal eine C API nutzen muss. Aber das ist kein Anfängerthema. In der Regel sollte Speicher eine Containerklasse z.B. vector oder array genutzt werden. Geht das nicht und man muss doch Speicher anfordern, sollte man Smart Pointer nutzen. Unbedingt unique_ptr, shared_ptr und weak_ptr erlernen und wann man sie jeweils einsetzt. In diesem konkreten Fall, ist für einen Anfänger vector die sinnvollste Lösung. Falls es doch einmal Speicheranforderung via C API sein muss, sollte man das bitte so schreiben.

    int* p = static_cast<int*>(malloc(n * sizeof(int)));
    

    C Casts werden in C++ niemals verwendet. Dafür gibt es keinerlei Notwendigkeit.
    Aber wenn man schon manuell in C++ Speicher anfordert. Warum dann nicht die C++ Variante davon?

    int* p = new int[n];
    

    P.S. Manuelle Speicherverwaltung sollte man als Anfänger wirklich nur dann machen, wenn man es explizit lernen will. Bitte die Rule of Zero/Three/Five beachten und dann in Code umsetzen.

    Update: Klammern bei static_cast ergänzt.



  • @Mechanics sagte in Probleme mit dynamischen Arrays (malloc und free):

    Und das nächste ist, vielleicht willst du das ja später anders machen. Wenn deine Matrix intern malloc oder new[] macht, hast du keine Chance, das von außen zu ändern. Stell dir aber vor, dass du mal einen Pool Allokator verwenden willst...

    Du willst einem Anfänger die korrekte Nutzung von allocator_traits erklären? Das wird spannend.



  • Ich wollte nur auf den Grund hinaus, warum es erwünscht ist, solche Funktionalität zu trennen.
    Schließt natürlich nicht aus, dass sich daraus weitere Fragen ergeben.



  • @john-0 sagte in Probleme mit dynamischen Arrays (malloc und free):

    C Casts werden in C++ niemals verwendet. Dafür gibt es keinerlei Notwendigkeit.

    Genau!

    Und wenn @Rav1642 es sehr ausführlich erklärt/begründet haben will, hätte ich zu Casts ein Video von STL anzubieten: https://www.youtube.com/watch?v=6wdCqNlnMjo&t=393 Fängt bei 6 1/2 Minuten an, über Casts zu sprechen. Ab Minute 23 folgt dann die Begründung, warum C-Style Casts schlecht sind.


  • Mod

    Man kann's aber auch in ganz wenigen Sätzen zusammenfassen:

    1. Schlechtere Lesbarkeit, wo ein Cast geschieht, da es den gleichen Aufbau wie andere oft vorkommende Syntaxelemente hat. Man muss den Komplettausdruck im Kopf parsen, um den Cast sicher zu erkennen und kann auch nicht querlesen, um Kandidaten zu filtern.
    2. Wundertüte, was genau man bekommt, da jeder Cast potentiell eine Kombination von allen C++-Casts ist. Man muss den Ausdruck nicht nur komplett parsen, sondern auch im Kopf (korrekt!) Ableiten, was an welcher Stelle welchen Typ hat und welche Castregeln gelten, um sichere Aussagen zu tätigen.

    Ist bei einem Trivialausdruck wie (double) int_var vielleicht nicht ganz so schwerwiegend, aber man braucht keine unrealistischen Beispiele heranziehen, bevor die Nachteile relevant werden.



  • @SeppJ Das einzige was mit C-Style nicht geht ist dynamic_cast<> oder?



  • ...und wenn man mit Zeigern auf Objekte sowie Objekte abgeleiteter Klasse herumhantiert. U.u. muss dann ein Offset addiert werden, was bei einem C-Cast nicht passiert (aber z.B. mit static_cast).



  • @Swordfish sagte in Probleme mit dynamischen Arrays (malloc und free):

    @SeppJ Das einzige was mit C-Style nicht geht ist dynamic_cast<> oder?

    Doch, auch das geht.



  • @manni66 magic O.O



  • @manni66 sagte in Probleme mit dynamischen Arrays (malloc und free):

    @Swordfish sagte in Probleme mit dynamischen Arrays (malloc und free):

    @SeppJ Das einzige was mit C-Style nicht geht ist dynamic_cast<> oder?

    Doch, auch das geht.

    Wie denn?

    Der Standard sagt (Kapitel 5.4 in N4296), dass (T) const_cast, static_cast, static_cast gefolgt von const_cast, reinterpret_cast oder reinterpret_cast gefolgt von const_cast macht. Von dynamic_cast steht da nichts. Wie bringst du (T) dazu, sich wie dynamic_cast zu verhalten?



  • @wob sagte in Probleme mit dynamischen Arrays (malloc und free):

    @manni66 sagte in Probleme mit dynamischen Arrays (malloc und free):

    @Swordfish sagte in Probleme mit dynamischen Arrays (malloc und free):

    @SeppJ Das einzige was mit C-Style nicht geht ist dynamic_cast<> oder?

    Doch, auch das geht.

    Wie denn?

    Der Standard sagt (Kapitel 5.4 in N4296), dass (T) const_cast, static_cast, static_cast gefolgt von const_cast, reinterpret_cast oder reinterpret_cast gefolgt von const_cast macht. Von dynamic_cast steht da nichts. Wie bringst du (T) dazu, sich wie dynamic_cast zu verhalten?

    Hm, eigentlich war ich mir sicher, aber wenn es da so steht habe ich mich wohl getäuscht.


  • Mod

    Wäre ja auch komisch, wenn ein C-Feature (wo es dynamic_cast prinzipiell nicht geben kann) sich in C++ anders verhalten würde. Die von @wob zitierte Regel ist ja nur C++Sprachregelung zu dem Verhalten, wie es im C-Standard steht.



  • @SeppJ sagte in Probleme mit dynamischen Arrays (malloc und free):

    Wäre ja auch komisch,

    +1



  • @Mechanics sagte in Probleme mit dynamischen Arrays (malloc und free):

    Zur Speicherverwaltung gehört ja ein bisschen was dazu. Wenn du das manuell machst und das korrekt machen wolltest, noch mehr als das was du bisher gemacht hast. Das hat jetzt aber alles nichts mit der Klasse Matrix zu tun. Eine andere Klasse, die etwas ganz anderes macht, könnte evtl. genau die gleiche Logik zur Speicherverwaltung brauchen.

    Gut, danke. Jetzt möchte ich ja nur verschiedenste Anwendungen aus dem Bereich der linearen Algebra umsetzen. Ich habe aktuell gar nicht vor, in die Tiefen der Speicherverwaltung vorzudringen. Für meine Zwecke reicht nun in der neuesten Version auch ein eindimensionaler std::vector<double> für die Elemente der Matrix.

    @john-0 sagte in Probleme mit dynamischen Arrays (malloc und free):

    Du willst einem Anfänger die korrekte Nutzung von allocator_traits erklären? Das wird spannend.

    Na, du hast ja viel Vertrauen in mich. 😃
    Soweit ich weiß, reserviert ein Allocator Speicher:

    std::allocator<double> alloc;
    double *d = alloc.allocate(10);
    

    Ein Pool besteht aus im Vorfeld reservierte Ressourcen, damit während der Laufzeit aufwendigens Anfordern von Ressourcen ausbleibt.
    Einen pool allocator stelle ich mir demnach als einen Allocator vor, der ausreichend Speicher bereitstellt und während der Laufzeit stets neu verteilt. Ich hoffe, ich habs getroffen. 😅

    @wob Danke für das Video! 🙂

    Ich habe also etwas an meiner Matrix-Klasse gefeilt und möchte nochmal auf die Funktionalität zurück.
    Ich habe die Berechnung der Determinante außerhalb der Klasse. Sollte ich das mit anderen Methoden auch so handhaben?
    Hier ist die Matrix.h:

    #pragma once
    #include <vector>
    
    class Matrix {
    private:
    	std::vector<double> values;
    	int r; // number of rows
    	int c; // number of columns
    	int size;
    	double lowestVal;
    	double highestVal;
    
    public:
    	// constructor
    	Matrix(int rows, std::vector<double> vals);
    
    	// special methods
    	void print(int precision);
    	Matrix submatrix(int deletedRow, int deletedColumn);
    
    	// standard setter
    	void setValue(int row, int column, double newValue);
    	void setValue(int pos, double newValue);
    
    	// standard getter
    	double getValue(int row, int column);
    	double getValue(int pos);
    	double getLowestVal(); // lowest value
    	double getHighestVal(); // highest value
    	int getRows();
    	int getColumns();
    	int getSize(); // equivalent to rows * columns
    
    	// operator overloading
    	Matrix operator- (); //unary
    	friend Matrix operator+ (Matrix m, Matrix n);
    	friend Matrix operator- (Matrix m, Matrix n);
    	friend Matrix operator* (Matrix m, Matrix n);
    	
    	// destructor
    	~Matrix();
    };
    
    double determinant(Matrix m);
    

    Vielen Dank euch! 🙂



  • Du solltest Objekte bei Funktionsaufrufen per Referenz übergeben, nicht per Value. Wenn sie in der Funktion nicht verändert werden auch gern als const-Reference. Ansonsten wird jeder Parameter als Kopie erstellt, was bei std::vector schnell teuer werden kann.

    Ansonsten gibt´s hier im Forum eine fertige Implementation eines 2D-Arrays, vllt hilft dir das ja weiter.



  • @Rav1642 sagte in Probleme mit dynamischen Arrays (malloc und free):

    Ich habe die Berechnung der Determinante außerhalb der Klasse.

    Warum?

    Sollte ich das mit anderen Methoden auch so handhaben?

    Wenn du dir die Frage von oben beantwortest, dann weisst du doch auch, ob es bei anderen Funktionen ebenfalls sinnvoll ist.
    Allgemein wirst du aber mit freien nicht-friend Funktionen viel Zustimmung kriegen.
    Ich persönlich hätte aber auch mit einer const member function "determinant" kein Problem, aber wie gesagt, eine freie Funktion wird tendenziell bevorzugt
    (weil Meyers das sagt und man immer gut beraten ist, die Tipps zu beherzigen)


Anmelden zum Antworten