memset vs. {0}. Was nehmen?



  • Hi,

    ich habe folgende Frage: Welchen Unterschied gibt es zwischen folgenden beiden Varianten und welches Verfahren ist vorzuziehen, und vorallem: Warum?

    // Eine kleine Teststruktur.
    struct meine_struct
    {
        int x;
        int y;
    
        char text[512];
    
        float pi;
    
        struct
        {
            unsigned long wert1;
            unsigned long wert2;
            unsigned long wert3;
        } keine_ahnung;
    
        unsigned char* daten;
    };
    
    // Variante 1:
    meine_struct initialisierung_eins = { 0 };
    
    // Variante 2:
    meine_struct initialisierung_zwei;
    memset (&initialisierung_zwei, 0, sizeof (initialisierung_zwei);
    

    Dazu hab ich noch eine Frage: Warum sollte man folgendes nicht machen? Mir wurde immer gesagt, ich solls sein lassen, aber eine Begründung kam nie 😞

    klasse::klasse ()
    {
        memset (this, 0, sizeof (klasse));
    }
    

    Vielen Dank!



  • weil du nicht weißt ob die interne Representation der Daten wirklich einer 0 entspricht, daher kannst du mit memset leicht dir Daten zerschießen. Der Fall mit this ist besonders problematisch, da du so den vtable-Pointer killst!



  • solange "klasse" ein POD ist ist alles gut.
    unschön ist es trotzdem.
    sobald "klasse" kein POD ist ist es einfach falsch, aus ende, was soll man da weiter begründen?



  • hustbaer schrieb:

    solange "klasse" ein POD ist ist alles gut.

    Selbst dann ist es nicht gut, da die interne Darstellung der Typen anders sein kann (zB könnte ein Pointer zusätzliche Informationen oder Fließkommazahlen könnten ganz andere Formatierungen enthalten etc.)



  • hustbaer schrieb:

    sobald "klasse" kein POD ist ist es einfach falsch, aus ende, was soll man da weiter begründen?

    Sorry, aber da ich lernen will, lasse ich mich natürlich nicht mit "was soll man da weiter begründen?" abstempeln, vorallem da ich als Noob nicht weiß, was ein POD sein soll.

    Daher bitte ich um etwas ausführlichere Infos, damit ich deiner Signatur treu werden kann: "Lern C++!" 😉 Dafür benötige ich halt bessere infos als nur "was soll man da weiter begründen?" 😉

    @rüdiger:
    Also was ist vorzuziehen? Memset oder { 0 }. ihr beide sprecht für mich irgendwie in rätseln 😞



  • Gar nichts von beidem => alle Member einzeln setzen.



  • Rüdiger sagte bereits, dass du mit memset den vtable zerstörst.
    Also, wenn du nicht weißt, was ein(e) vtable ist: Klicke hier!.

    Gut es funktioniert teilweise mit PODs, aber darauf solltest du dich nicht verlassen.

    Initialisiere die Membervariablen doch einfach im Konstruktor.

    Gruß
    Don06


  • Mod

    memsetter schrieb:

    hustbaer schrieb:

    sobald "klasse" kein POD ist ist es einfach falsch, aus ende, was soll man da weiter begründen?

    Sorry, aber da ich lernen will, lasse ich mich natürlich nicht mit "was soll man da weiter begründen?" abstempeln, vorallem da ich als Noob nicht weiß, was ein POD sein soll.

    Daher bitte ich um etwas ausführlichere Infos, damit ich deiner Signatur treu werden kann: "Lern C++!" 😉 Dafür benötige ich halt bessere infos als nur "was soll man da weiter begründen?" 😉

    @rüdiger:
    Also was ist vorzuziehen? Memset oder { 0 }. ihr beide sprecht für mich irgendwie in rätseln 😞

    Vergiss memset. Es hat keinerlei Vorteile, macht potentiell das Falsche oder etwas Undefiniertes oder mehr als notwendig und lenkt vom eigentlichen Problem, an dem du arbeitest, ab. Zudem erhöht es die Anzahl potentieller Fehlerquellen, die Parameter richtig hinzubekommen (und dann auch noch so, dass man es später mit einem Blick verstehen kann), ist durchaus nicht trivial.

    Problem Nr. 1 (das Falsche)
    Wie Daten intern repräsentiert werden, ist weitgehend der Implementation überlassen. Nur weil du etwa einen Zeiger mit 0-Bytes überschrieben hast, bedeutet nicht, dass das jetzt zwingend ein NULL-Pointer ist. Wenn du aber nicht weißt, wohin der Zeiger anschließend zeigt, hat dir das Überschreiben überhaupt nichts genützt, dann hättest du es auch gleich bleiben lassen können.

    Problem Nr. 2 (das Undefinierte)
    Sobald du es nicht mit PODs (google ist dein Freund) zu tun hast, wird das Verhalten gänzlich unbestimmt, erst recht solche Sachen wie ein memset im Konstruktor. Praktisch gibt es da Grenzfälle und zudem wird die Definition von PODs in C++0x etwas weiter ausfallen - aber selbst wenn du genau weißt, was du da tust, ist kaum memset kaum nützlicher als richtige Initialisierung.

    Problem Nr.3 (zuviel)
    Klassen können Paddingbytes besitzen, die du mit memset mit überschreibst, dass ist mehr als du tun musst und daher potentiell Zeitverschwendung.

    Zudem ist die Verwendung von memset so etwas ähnliches wie dynamische Initialisierung, für statische Objekte etwa ist das unzweckmäßig. Auch muss der Compiler sehr gewitzt sein, um zu erkennen, was passiert, während es bei einer direkten Initialisierung ganz klar ist, welche Werte die einzelnen Member haben.

    { 0 } ist übrigens nicht die einzige sinnvolle Variante. Zunächst einmal ist die 0 überflüssig. Sie kann nur deshalb praktisch universell eingesetzt werden, weil fast alles, was man üblicherweise in Aggregaten findet (irgendwelche arithmetischen Typen oder Zeiger), durch 0-Konstanten initialisiert werden kann. Es kann also besser sein, einfach nur {} zu schreiben.
    Eine andere Möglichkeit, ist einfach ein defaultkonstrukiertes Objekt zu kopieren:

    T x = T();
    

    Das ist wirklich universell für alle Typen ohne Beschränkung auf Aggregate, die kopierbar sind. Und für moderne Compiler ist das genauso effizient wie direkte Initialisierung.



  • Ok, an Zeiger hatte ich nicht gedacht *pfeiff*
    Andrerseits sollte es OK sein einen Zeiger mit memset() zu bearbeiten solange man ihn danach schreibt bevor man ihn liest/gegen 0 prüft.

    floats/doubles sind kein Problem, da der Standard das Speicherabbild von float und double vorschreibt, und es ist nunmal so dass da "alles 0 Bits" == "0.0".

    @memsetter:
    Der Standard sagt selten etwas über die Gründe warum irgendetwas "verboten" ist, bzw. warum irgendetwas "undefined behaviour" bzw. "implementation defined" verursacht/ist.

    Wenn du den Standard mal aussen vor lassen willst, und wissen warum es mit einem bestimmten Compiler nicht schlau ist: nun, da wurde schon einiges genannt. Der Zeiger auf den vtable der von vielen (allen?) Compilern für virtuelle Funktionen verwendet wird sollte nicht verändert werden, genauso eventuelle andere "innere Zeiger" (oder sonstige Daten) die vielleicht für virtuelle Vererbung oder ähnliches verwendet werden.

    Sämtliche integers, floats/doubles und Zeiger sind dagegen auf den meisten Compilern kein Problem.

    Und: was ein POD ist kann ich nicht in einem Satz zusammenfassen. Die C++ FAQ Lite sagt folgendes: http://www.parashift.com/c++-faq-lite/intrinsic-types.html#faq-26.7

    BTW: du solltest dir nen Bookmark setzen: http://www.parashift.com/c++-faq-lite/


  • Mod

    Die Erklärung hinter dem Link ist allerdings nicht richtig. pointer-to-member sind sehr wohl erlaubt [3.9/10] - logischerweise haben die allerdings kein Äquivalent in C.



  • zudem wird die { 0 } variante mit dem nächsten standard wohl die bei weitem favorisierte methode der objektinitialisierung sein. ich freu mich drauf(auch wenn ich mir gewünscht hätte, dass es ein wenig weiter ausgearbeitet worden wäre...daraus hätte man soviel machen können)


Anmelden zum Antworten