Probleme mit dynamischen Arrays (malloc und free)



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

    jedoch hatte ich kürzlich gelesen, dass malloc und free auch in C++ problemlos funktionieren.

    ja vor 30 jahren oder so wollte man durch diese kompatibilität zu C den umstieg erleichtern. aber das funktioniert nicht immer bzw. führt zu merkwürdigen fehlern, und deshalb sollte man das lassen.

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

    klausurfrage: finden sie den fehler in dieser anweisung!



  • @Wade1234
    Mit

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

    hatte ich keine Probleme, das funktioniert ohne Fehler.
    Malloc gibt ja einen void-Zeiger zurück. Den muss man dann explizit casten. 2*sizeof(...), weil ich in diesem Mini-Programm eine feste 2x2-Matrix hatte.
    Ich weiß daher nicht, worauf du hinaus willst.

    Grüße
    Pascal



  • @Rav1642
    naja worauf zeigt k nach dem aufruf von malloc?



  • @Wade1234
    Ich nehme an, dass k dann auf die Speicheradresse zeigt, ab der der reservierte Speicherplatz mit Größe 2 * sizeof(int **) beginnt. Nicht?



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


Anmelden zum Antworten