malloc oder new



  • @_ro_ro sagte in malloc oder new:

    Und dieser von mir ausgeheckte Algorithmus bewährt sich seit 20 Jahren

    Um mal auf deine Frage malloc oder new? zurückzukommen.

    In halbwegs moderneren C++ nutzt man new nur noch sehr sehr selten und dann nur in gut begründeten Ausnahmefällen. Das Schlüsselwort malloc gar nicht mehr.

    Manuelle Speicherverwaltung mittels new oder malloc,... ist nämlich sehr fehleranfällig. Einmal ein delete oder free vergessen und schon existiert Speicher, welcher nicht freigegeben und nicht zugreifbar ist. Passiert dies periodisch, wird dein Programm oder System abstürzen. Stell dir z.B. vor deine Funktion read_eav_file würde 20 return Anweisungen enhalten. Wie sähe dann korrekter Code aus?

    Oder man ruft deleteanstatt delete[] auf. Oder man ruft malloc für Klassen auf. Alles vermeidbare Fehler.

    Deswegen existieren in C++ Klassen wie std::vector, std::string, std::unique_ptr, std::shard_ptr welche nur die Aufgabe der Ressourcenverwaltung haben (siehe auch RAII). Damit werden Ressourcen automatisch verwaltet und freigeben. Speicherlöcher passieren damit gar nicht mehr.

    Die Frage malloc oder new? beantwortet sich also folgendermaßen: Nutze weder das eine noch das andere, sondern nutze die Containers library oder Memory management library von C++.

    Siehe auch:
    https://en.cppreference.com/w/



  • @Quiche-Lorraine

    danke Quiche Lorraine für Ihre ausführliche und verständliche Antwort. Vector-Objekte sind eine feine Sache, wieder was dazugelernt (und auch schon nachgelesen). In Fakt habe ich noch einige Zeilen ANSI-C in meinem Code zu überarbeiten. C++ ist mächtig gewaltig, da fange ich gerade erst an 😉

    Viele Grüße, schönen Sonntag und kommen Sie gut ins Neue Jahr!


  • Gesperrt

    @_ro_ro sagte in malloc oder new:

    Mein Serializer entrollt gar nichts.

    Ok, wenn du das selber nicht erkennst, dann will ich nix gesagt haben... Schönen Abend noch.



  • @nameName

    Mein Algorithmus beherzigt das was Niklaus Wirth mal schrieb, nämlich daß Dateien Sequenzen sind und von daher sequentiell gelesen und geschrieben werden. Wenn Sie sich dagegen die Exif-Spezifikation anschauen, da sind die Exif-Daten in der ganzen Bilddatei verstreut. Und das ist äußerst ineffizient, denn anstatt die Daten sequentiell nacheinander einzulesen, muß der Dateizeiger irrsinnig hin und her bewegt werden.

    Genau das sollte ein Deserialize-Algorithmus ganz sicher nicht tun. Und jetzt sind Sie mal so gut und schauen sie sich meinen Algorithmus noch einmal richtig an. Der macht genau das und rennt straight forward durch die Datei ohne sich nur einmal umzudrehen.

    MFG

            // Algorithmus: Lese 3 mal jeweils 4 Bytes
            while( fread(lens,4,3,fp) ){
                int elen = endian( lens[0] );
                int alen = endian( lens[1] );
                int vlen = endian( lens[2] );
    
                vector <char> ent( 1 + elen );
                vector <char> att( 1 + alen );
                vector <char> val( 1 + vlen );
                fread(ent.data(), elen, 1, fp);
                fread(att.data(), alen, 1, fp);
                fread(val.data(), vlen, 1, fp);
                ent[elen] = 0;
                att[alen] = 0;
                val[vlen] = 0;
                string entity = ent.data();
                string attribute = att.data();
                string value = val.data();
                eav[entity][attribute] = value;
            }
    
    

  • Gesperrt

    @_ro_ro sagte in malloc oder new:

    muß der Dateizeiger irrsinnig hin und her bewegt werden.

    Für heutige SSDs ist es wahrscheinlich nicht wichtig, ob einmal viele Daten sequenziell gelesen werden oder zwei unzusammenhängende, kleinere Datenblöcke gelesen werden.

    Und ja, die innere Schleife wurde entrollt, und das ist ja nichts schlechtes.



  • @nameName sagte in malloc oder new:

    @_ro_ro sagte in malloc oder new:

    muß der Dateizeiger irrsinnig hin und her bewegt werden.

    Für heutige SSDs ist es wahrscheinlich nicht wichtig, ob einmal viele Daten sequenziell gelesen werden oder zwei unzusammenhängende, kleinere Datenblöcke gelesen werden.

    Das mag sein. Es ist jedoch unsinnig, Random Access auf Byte-Ebene abzubilden, genau darauf läuft es ja hinaus bei Exif. Und das ist Schrott.

    Und ja, die innere Schleife wurde entrollt, und das ist ja nichts schlechtes.

    Wo bitte sehen Sie denn in meinem DeSerialize-Algorithmus eine innere Schleife!?

    MFG


  • Gesperrt

    Theoretisch wäre dort eine, die von 0 bis einschließlich 2 läuft. Ist aber für Anfänger nicht wichtig.

    @_ro_ro sagte in malloc oder new:

    Es ist jedoch unsinnig, Random Access auf Byte-Ebene abzubilden, genau darauf läuft es ja hinaus bei Exif.

    Das lasse ich mal unkommentiert. Bei 100 GB Daten wäre wahlfreier Zugriff schon ganz nett. Bei ein paar Kilobyte ist das egal.



  • @_ro_ro. Wenn du letztlich die Daten sowieso als string haben willst, dann solltest du jedoch nicht den Weg über vector<char> gehen, da sonst unnötigerweise die Daten jeweils nochmal kopiert werden (und die lokalen Objekte erzeugt und wieder gelöscht werden).
    Dann kannst du gleich für die 3 Daten es so benutzen (wie schon von @hustbaer geschrieben):

    string s;
    s.resize(len);
    fin.read(&s[0], len); // oder s.data()
    

    oder alternativ

    string s(len, '\0');
    fin.read(&s[0], len); // oder s.data()
    

    Und fin ist dann ein std::ifstream, anstatt Nutzung des C-Datentyps FILE*.



  • @Th69

    resize() ja. Aber bei

    eav[entity][attribute] = value;
    

    müssen 3 Strings vorliegen. Immerhin: malloc ist raus aus meinem Code. Und ob ich <fstream> verwende oder legacy FILE* ist letztendlich Wurst.

    Viele Grüße!



  • @Th69

    Ok, die Terminierung gleich bei der string-Erstellung und beim Lesen den Adressoperator:

        // Routingtable Binary einlesen
        void thaw( map< string, map<string, string> > &eav ){
            // mode "rb": lesen in binary mode
            FILE *fp = fopen(BINPATH, "rb");
            if(fp == NULL){
                throw string("Error open File dbf.bin");
            }
            // Puffer für die Längenangaben
            uint32_t lens[3];
            // Algorithmus: Lese 3 mal jeweils 4 Bytes
            while( fread(lens,4,3,fp) ){
                // Net-Order zu Vax-Order
                int elen = endian( lens[0] );
                int alen = endian( lens[1] );
                int vlen = endian( lens[2] );
                string ent(elen, '0');
                string att(alen, '0');
                string val(vlen, '0');
                fread(&ent[0], elen, 1, fp);
                fread(&att[0], alen, 1, fp);
                fread(&val[0], vlen, 1, fp);
                eav[ent][att] = val;
            }
            fclose(fp);
        }
    
    Läuft sauber aber sowas von ;)
    
    Vielen Dank!!!
    


  • Die '\0' hat hier aber nichts mit Terminierung zu tun, sondern ist einfach nur der Füllwert.
    Erst wenn .c_str() aufgerufen wird, wird das Terminalzeichen angefügt (bzw. intern in der Implementierung gleich mit beachtet).
    Und so kann ein string auch '\0'-Zeichen intern speichern (d.h. auch binär einlesen), was in deiner vorherigen Implementierung bisher nicht ging - auch wenn deine Daten diese wohl nicht enthalten.



  • @nameName

    offensichtlich haben Sie den Niklaus Wirth (Algorithmen und Datenstrukturen mit Modula - 2 ) nicht so richtig verstanden. Btw., gemäß der Wirth'schen Prinzipien habe ich eine Webanwendung entwickelt die Fileuploads von > 500 Dateien auf einen Rutsch und einer Datenmenge jenseits von 1 GB ermöglicht. Mit einem prorietären Enctype der nicht multipart/form-data heißt und Random Access da wo er hingehört: RAM.

    MFG



  • @Th69 sagte in malloc oder new:

    Und so kann ein string auch '\0'-Zeichen intern speichern

    Ja schon. Aber mein Test ergab, daß length() 1. nicht die gesamte Länge liefert sondern nur die Anzahl der Bytes bis '\0' und 2. wird ein solcher String auch nicht in seiner gesamten Länge ausgegeben sondern nur bis das Byte mit der Wertigkeit 0 kommt.

    Viele Grüße!



  • Dann hast du falsch getestet:

    string s;
    s += '1';
    s += '\0';
    s += '2';
     
    cout << s << endl;
    cout << "Length: " << s.length() << endl;
    

    Ausgabe:

    12
    Length: 3
    

    s. Ideone-Code

    Du darfst selbstverständlich nicht ein direktes C-String-Literal verwenden:

    string s = "1\02";
    

    denn dieser Konstruktoraufruf liest nur bis zum ersten Nullterminalzeichen (entsprechend der Definition eines C-String-Literals).



  • @Th69

    Ok, jetzt alles klar. So hatte ich letzteres "foo\0bar"; getestet mit dem bekannten Ergebnis 😉

    Viele Grüße!!!


  • Gesperrt

    @_ro_ro sagte in malloc oder new:

    @nameName
    offensichtlich haben Sie den Niklaus Wirth (Algorithmen und Datenstrukturen mit Modula - 2 ) nicht so richtig verstanden.

    Was soll diese Provokation? Lass mich doch in Ruhe, wenn du alles besser kannst.


  • Gesperrt

    Aber, um dennoch auf deine Frage einzugehen ... der Cormen ist das "Standardwerk", wenn es um Algorithmen geht. Und den habe ich gelesen, ja.



  • Hallo Ihr Lieben 😉

    falls es Missverständnisse gab bitte ich um Entschuldigung. Ansonsten habe ich dank Eurer Hilfe meinen Code von malloc befreit. Falls ich dennoch new verwende, wäre das ok in Verbindung mit delete?

    Viele Grüße!!!



  • Klar ist das möglich, aber das sollte wenn überhaupt nur in einer Klasse geschehen, die explizit für Ressourcen-Management da ist (und nichts anderes tut, und da nimmt man normalerweise die aus der Standardbibliothek)

    Siehe auch https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#p8-dont-leak-any-resources

    Und speziell für new/delete: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r11-avoid-calling-new-and-delete-explicitly

    Insbesondere gilt das auch für das fstream vs fopen oben. Wenn du mit fopen öffnest, darfst du das fclose nicht vergessen. Wenn vorher eine Exception fliegt oder du returnst, kann das fclose vergessen werden. Die fstreams dagegen schließen im Destruktor automatisch.

    Wie andere schon gesagt haben: die Idee ist RAII: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r1-manage-resources-automatically-using-resource-handles-and-raii-resource-acquisition-is-initialization



  • @wob

    Vielen Dank!!! Das eigentliche Problem ist, daß mein Konzept noch nicht so richtig ausgereift ist. Ich denke jedoch daß ich auf dem richtigen Weg bin 😉

    Viele Grüße!


Anmelden zum Antworten