Hallo Leute, bei uns in der Firma ist es "Konvention", Strukturen mit memset zu initialisieren. Also z.B. struct myStruct { ... ... ... }; myStruct einsDavon; memset( &einsDavon, (int)'\0', sizeof(myStruct) ); Dazu 2 Fragen: 1) Was soll der (int) Cast? warum nicht einfach: memset( &einsDavon, 0, sizeof(myStruct) ) 2) Es gibt Kollegen, die die ganze memset Initialisierung für Quatsch halten, für andere ist es unabdingbar. Was spricht dagegen? Als Berufsanfänger kann ich den Glaubenskrieg nicht richtig einordnen. Wäre dankbar für ein paar Meinungen dazu. Gruß Thilo
Thilo schrieb: > Dazu 2 Fragen: > > 1) Was soll der (int) Cast? warum nicht einfach: keine Ahnung - man braucht ihn zumindest nicht. > memset( &einsDavon, 0, sizeof(myStruct) ) ja ist ok > 2) Es gibt Kollegen, die die ganze memset Initialisierung für Quatsch > halten, für andere ist es unabdingbar. > Was spricht dagegen? es spricht dagegen das es zeit und resourcen kostet, dafür spricht das man definierten werte am anfang hat.
Peter II schrieb: > ... vielen Dank für die schnelle Antwort. Als Anfänger bin ich etwas unsicher gegenüber den "alten Hasenn". Deine Antwort bestärkt mich darin, es so zu machen, wie ich es "gelernt" habe und seit Jahren selbst praktiziere. Gruß Thilo
Möchte man so eine Struktur benutzen, ist es schon sinnvoll, wenn sie mit definierten Werten gefüllt ist. Eine Initialisierung ist also sinnvoll. Allerdings ist eine explizite Initialisierung mit memset nicht immer nötig, je nachdem, woher der Speicher kommt, in dem die Struktur liegt. Statische Variablen werden von der Laufzeitumgebung mit 0 initialisiert. Automatische Variablen (die auf dem Stack angelegt werden und nur für die Dauer der Gültigkeit des sie enthaltenen Blocks existieren) werden nicht von der Laufzeitumgebung initialisiert und enthalten daher mehr oder weniger zufällige Daten. malloc liefert uninitialisierten Speicher, calloc initialisiert den Speicher mit 0. Also ist der Gebrauch von memset bei Strukturen dann sinnvoll, wenn sie a) als automatische Variable auf dem Stack oder b) mit malloc angelegt werden. > myStruct einsDavon; > memset( &einsDavon, (int)'\0', sizeof(myStruct) ); Statt des Typecasts und der char-Konstante kann man hier vollkommen legal und mit exakt gleicher Auswirkung auch die int-Konstante 0 verwenden.
Alle struct- Variablen kann man aber auch mit ={0} initialisieren, das spart den Funktionsaufruf. memset würde ich nur für Objekte nehmen, die mit malloc beschafft werden. In C++ macht man das sowieso über einen Konstruktor. Generell ist eine konsequente Initialisierung ein wichtiger Schritt weg vom Pfusch.
Peter II schrieb: > Thilo schrieb: >> Dazu 2 Fragen: [snip] >> 2) Es gibt Kollegen, die die ganze memset Initialisierung für Quatsch >> halten, für andere ist es unabdingbar. > >> Was spricht dagegen? > es spricht dagegen das es zeit und resourcen kostet, dafür spricht das > man definierten werte am anfang hat. Wobei "definierte Werte" auch nur meistens hinhaut, Stichwort Portabilität. Ein Pointer mit allen Bits 0 ist nicht notwendigerweise der Nullpointer, ein float oder double mit allen Bits 0 meines Wissens auch nicht notwendigerweise 0.0. Hach, ein Struct mit 0-Bytes vollzupumpen könnte auch irgendwelche Padding-Bits auf eine Trap-Representation setzen. Nachzulesen im C99-Standard.
Jasch schrieb: > Wobei "definierte Werte" auch nur meistens hinhaut, Stichwort > Portabilität. > > Ein Pointer mit allen Bits 0 ist nicht notwendigerweise der Nullpointer, > ein float oder double mit allen Bits 0 meines Wissens auch nicht > notwendigerweise 0.0. > > Hach, ein Struct mit 0-Bytes vollzupumpen könnte auch irgendwelche > Padding-Bits auf eine Trap-Representation setzen. > > Nachzulesen im C99-Standard. Das ist natürlich prinzipiell richtig. Aber mal ehrlich: Im industriellen Bereich (also im wirklichen Leben von 99% aller Ingenieure) ist Portabilität kein Thema.
Schon mal so probiert? myStruct einsDavon = {} bzw. in C: myStruct einsDavon = {0}
!low level schrieb: > Schon mal so probiert? Ich wusste nicht, daß diese Syntax auf Strukturen anwenbar ist. Das ist natürlich die eleganteste Möglichkeit. Vielen Dank für den Tipp!
Rufus Τ. Firefly schrieb: > Möchte man so eine Struktur benutzen, ist es schon sinnvoll, wenn sie > mit definierten Werten gefüllt ist. Eine Initialisierung ist also > sinnvoll. Allerdings ist nicht generell für jedes Element jeder Struktur null ein sinnvoller Wert. Besser ist es daher, sie so weit wie möglich gleich richtig zu befüllen, statt sie erstmal blind mit Nullen zu füllen, die dann eh gleich wieder überschrieben werden. > Also ist der Gebrauch von memset bei Strukturen dann sinnvoll, wenn > sie a) als automatische Variable auf dem Stack oder b) mit malloc > angelegt werden. Bei automatischen Variablen würde aber auch eine explizite Initialisierung gehen.
1 | myStruct einsDavon = { 0 }; |
alle folgenden Elemente werden dabei automatisch 0-initialisiert. Es wäre also höchstens bei malloc sinnvoll, und da kann ich dann auch gleich calloc stattdessen verwenden. > Statt des Typecasts und der char-Konstante kann man hier vollkommen > legal und mit exakt gleicher Auswirkung auch die int-Konstante 0 > verwenden. '\0' ist eh schon nur in C++ eine char-Konstante. In C ist sie bereits vom Typ int. Aber so oder so kommt das selbe Ergebnis raus. Von der Code-Lesbarkeit her halte ich es aber für ziemlich unsinnig, '\0' zu verwenden, da das sugerriert, das Zielobjekt sei ein String.
Thilo schrieb: > Jasch schrieb: >> Wobei "definierte Werte" auch nur meistens hinhaut, Stichwort >> Portabilität. >> >> Ein Pointer mit allen Bits 0 ist nicht notwendigerweise der Nullpointer, >> ein float oder double mit allen Bits 0 meines Wissens auch nicht >> notwendigerweise 0.0. >> >> Hach, ein Struct mit 0-Bytes vollzupumpen könnte auch irgendwelche >> Padding-Bits auf eine Trap-Representation setzen. >> >> Nachzulesen im C99-Standard. > > Das ist natürlich prinzipiell richtig. Aber mal ehrlich: Im > industriellen Bereich (also im wirklichen Leben von 99% aller > Ingenieure) ist Portabilität kein Thema. Das ist schon richtig. Ich fände es allerdings extrem beruhigend wenn dieses Thema zumindest allen Ingenieuren bekannt wäre - was es leider wohl nicht ist. Die Idee dass so ein PC-Fuzzi wegen Jobwechsels neuerdings die Steuerung des ABS in meinem Auto mit 99%-PC-Methoden programmiert (hat doch immer so funktioniert...) löst irgendwie Unbehagen aus...
Thilo schrieb: > myStruct einsDavon; > memset( &einsDavon, (int)'\0', sizeof(myStruct) ); schöner wäre auf jeden Fall:
1 | myStruct einsDavon; |
2 | memset( &einsDavon, (int)'\0', sizeof(einsDavon) ); |
3 | // ^^^^^^^^^
|
bzw:
1 | myStruct* einPointerDavon; |
2 | // ...
|
3 | memset( einsDavon, (int)'\0', sizeof(*einPointerDavon) ); |
4 | // ^^^^^^^^^
|
der int cast ist in jedem Fall überflüssig Btw: in einem Sizeof darf man auch einen null-pointer oder einen ungültigen pointer dereferenzieren (schließlich ist es keine echte Dereffernzierung, da der Compiler das Sizeof zur Compilezeit berechnet). zb:
1 | myStruct* einPointerDavon; |
2 | einPointerDavon = (myStruct*) malloc(sizeof(*einPointerDavon)); |
3 | memset( einsDavon, (int)'\0', sizeof(*einPointerDavon) ); |
4 | // ^^^^^^^^^
|
das verhindert bei Ändern des Types der Variable einPointerDavon, dass an x Stellen der Typ ersetzt werden muss und man das irgendwo vergisst
Jasch schrieb: > Die Idee > dass so ein PC-Fuzzi wegen Jobwechsels neuerdings die Steuerung des ABS > in meinem Auto mit 99%-PC-Methoden programmiert (hat doch immer so > funktioniert...) löst irgendwie Unbehagen aus... Du brauchst halt ein entsprechend modernes Auto, dann erscheint auf dem zentralen Display die Meldung "bitte bremsen sie nicht, bis das ABS-System neu gebootet hat"... Gruss Reinhard
Jasch schrieb: > Ich fände es allerdings extrem beruhigend wenn dieses Thema zumindest > allen Ingenieuren bekannt wäre - was es leider wohl nicht ist. Die Idee > dass so ein PC-Fuzzi wegen Jobwechsels neuerdings die Steuerung des ABS > in meinem Auto mit 99%-PC-Methoden programmiert (hat doch immer so > funktioniert...) löst irgendwie Unbehagen aus... Ok, hab ich kapiert :-)
Vlad Tepesch schrieb: > schöner wäre auf jeden Fall: > myStruct einsDavon; > memset( &einsDavon, (int)'\0', sizeof(einsDavon) ); > // ^^^^^^^^^ > > > bzw:myStruct* einPointerDavon; > // ... > memset( einsDavon, (int)'\0', sizeof(*einPointerDavon) ); > // ^^^^^^^^^ > > der int cast ist in jedem Fall überflüssig > > Btw: > in einem Sizeof darf man auch einen null-pointer oder einen ungültigen > pointer dereferenzieren (schließlich ist es keine echte > Dereffernzierung, da der Compiler das Sizeof zur Compilezeit berechnet). > zb:myStruct* einPointerDavon; > einPointerDavon = (myStruct*) malloc(sizeof(*einPointerDavon)); > memset( einsDavon, (int)'\0', sizeof(*einPointerDavon) ); > // ^^^^^^^^^ > > das verhindert bei Ändern des Types der Variable einPointerDavon, dass > an x Stellen der Typ ersetzt werden muss und man das irgendwo vergisst Guter Punkt, vielen Dank!
Jasch schrieb: > Wobei "definierte Werte" auch nur meistens hinhaut, Stichwort > Portabilität. > > Ein Pointer mit allen Bits 0 ist nicht notwendigerweise der Nullpointer, > ein float oder double mit allen Bits 0 meines Wissens auch nicht > notwendigerweise 0.0. > > Hach, ein Struct mit 0-Bytes vollzupumpen könnte auch irgendwelche > Padding-Bits auf eine Trap-Representation setzen. > > Nachzulesen im C99-Standard. Das interessiert mich jetzt näher. Ich habe ein bischen dazu recherchiert. Meistens fand ich was zu Zahlen in anderer Darstellung, also 1er/2er-Komplement etc. Natürlich kann Float auch anders definiert sein als IEEE754. Soweit so gut. Somit wäre das Problem, das RAM der Daten wird mit Bits gefüllt, beim Zugriff auf das RAM mit Interpretation des Datentyps (Float bspw.) gibt es dann ein Problem. Du sprichst allerdings auch padding bei Strukturen an. Das wäre dann allerdings ein anderes Problem, richtig? Allerding erfolgt später kein Zugriff auf diese Padding-Bits, weil sonst wären das ja Daten. Und zur praktischen Veranschaulichung, kennst du eine Plattform bei der sowas tatsächlich auftritt? Vielen Dank für diesen Punkt, man lernt immer dazu!
BTW, die Initialisierung in "C" mit mystruct s = {0}; funktioniert nur wenn dem ersten Element der Struktur auch ein integer 0 zugewiesen werden kann. In "C++" geht es jedoch mit s = {} immer.
schmitti schrieb: > BTW, > die Initialisierung in "C" mit > mystruct s = {0}; > funktioniert nur wenn dem ersten Element der Struktur auch ein integer 0 > zugewiesen werden kann. In C gilt das für jeden skalaren Datentyp. Für die anderen wird dann noch ein weiteres Klammernpaar fällig.
Karl schrieb: > Jasch schrieb: >> Wobei "definierte Werte" auch nur meistens hinhaut, Stichwort >> Portabilität. >> >> Ein Pointer mit allen Bits 0 ist nicht notwendigerweise der Nullpointer, >> ein float oder double mit allen Bits 0 meines Wissens auch nicht >> notwendigerweise 0.0. >> >> Hach, ein Struct mit 0-Bytes vollzupumpen könnte auch irgendwelche >> Padding-Bits auf eine Trap-Representation setzen. >> >> Nachzulesen im C99-Standard. > > Das interessiert mich jetzt näher. Ich habe ein bischen dazu > recherchiert. Meistens fand ich was zu Zahlen in anderer Darstellung, > also 1er/2er-Komplement etc. Natürlich kann Float auch anders definiert > sein als IEEE754. Naja, das ist ja schon fast alles was so ein Computer verarbeiten kann, alles weitere (Zeichen, Pixel, ...) ist dann ja nur noch Interpretationssache. > Soweit so gut. Somit wäre das Problem, das RAM der Daten wird mit Bits > gefüllt, beim Zugriff auf das RAM mit Interpretation des Datentyps > (Float bspw.) gibt es dann ein Problem. Oder auch beim Verschicken übers Netzwerk u.ä., da ist der ganz offensichtliche Fall unterschiedliche Endianness (siehe ntohs(), ntohl() usw.). > Du sprichst allerdings auch padding bei Strukturen an. Das wäre dann > allerdings ein anderes Problem, richtig? Allerding erfolgt später kein Richtig. > Zugriff auf diese Padding-Bits, weil sonst wären das ja Daten. Durch das C-Programm nicht, durch die Hardware vielleicht schon. Stell Dir z.B. RAM mit 8 Bits/Byte plus einem Parity-Bit vor, wobei sowas fuer C normalerweise transparent wäre. > Und zur praktischen Veranschaulichung, kennst du eine Plattform bei der > sowas tatsächlich auftritt? Nein, Gott sei dank! Sowas ist meines Wissens eher unüblich, jedenfalls innerhalb der heutigen Mainstream-Hardware. Das Parity-Problem hat mich vor vielen Jahren mal in den Hintern gebissen, da habe ich vom BIOS nicht eingeschalteten RAM (oberhalb 640K, ja das war noch unter DOS ;-) eingeschaltet. Die Hardware hatte Parity fürs RAM, ziemlich jeder Lesezugriff auf den "neuen" Speicher ging schief bis ich den erstmal mit Nullen vollgeschrieben habe um die Parity-Bits zu initialisieren. Hmm, ich denke mir mal ein Szenario aus: eine Hardware mit 32-Bit-Speicherbus, mit einem 24-Bit-Integer-Typen - manche DSPs haben so einen - mit Alignment 4 Byte, der Hardware-Designer war voll auf Drogen und deshalb will die Hardware die obersten 4 Bits als 0xf sehen - sonst gibt es einen Zugriffsfehler. Ein Füllen eines structs mit Nullen wird hier nicht funktionieren wenn es einen dieser 24-Bit-Integer enthält. Würde jemand solche Hardware designen? Hmm, unmöglich ist nix, die Hardware-Entwickler nehmen manchmal erstaunlich wenig Rücksicht auf die SW-Entwickler die mit dem Zeug dann was machen sollen.
Rolf Magnus schrieb: > schmitti schrieb: >> BTW, >> die Initialisierung in "C" mit >> mystruct s = {0}; >> funktioniert nur wenn dem ersten Element der Struktur auch ein integer 0 >> zugewiesen werden kann. > > In C gilt das für jeden skalaren Datentyp. Für die anderen wird dann > noch ein weiteres Klammernpaar fällig. AFAIK wird der Compiler bei einem enum aber meckern, dass der Typ nicht kompatibel ist, und ein Cast wäre irgendwie Pfusch.
Ganz abgesehen von der Korrektheit des Programms ist Initialisierung mit Nullen auch wichtig für Datenstrukturen, die irgendwann in einer Datei enden oder übers Netzwerk gesendet werden. Wenn man nur die Felder einzeln initialisiert, bleiben Padding, nicht voll ausgenutzte String-Elemente (v.a. bei Initialisierung mit einem leeren String) und sonstige Arrays, in der nur genutzte Felder initialisiert werden, unangetastet. Soll heißen sie enthalten irgendwelche Daten, die vorher im Speicher an der Stelle standen. Das kann alles von belanglos bis extrem sensibel sein. Ein Grund warum man Datenfelder die Passwörter o.ä. enthalten auch vor dem Freigeben noch mal überschreiben sollte.
Andreas B. schrieb: > Ganz abgesehen von der Korrektheit des Programms ist Initialisierung mit > Nullen auch wichtig für Datenstrukturen, die irgendwann in einer Datei > enden oder übers Netzwerk gesendet werden. Wenn man nur die Felder > einzeln initialisiert, bleiben Padding, nicht voll ausgenutzte > String-Elemente (v.a. bei Initialisierung mit einem leeren String) und > sonstige Arrays, in der nur genutzte Felder initialisiert werden, > unangetastet. Uhh, ohh, aber sowas zu machen ist von vornherein ein Designdesaster. Die In-Memory-Datenstrukturen und die übers Netzwerk verschickten oder auf die Platte geschriebenen Daten haben strukturell genau garnichts miteinander zu tun. Nur weil ich im Speicher einen 32-Bit-Int habe heisst noch nicht dass das im Netz nicht ein 16-Bit-Int sein kann, mit anderer Endianness, oder der Null-terminierte String im Speicher könnte im Netz einer im Pascal-Format sein, mit einem anderen Encoding obendrein. Jeder Versuch die Speicherdatenstrukturen sklavisch zur externen Datenrepräsentation identisch zu machen endet potentiell in der Katastrophe.
Jasch schrieb: > Die In-Memory-Datenstrukturen und die übers Netzwerk verschickten oder > auf die Platte geschriebenen Daten haben strukturell genau garnichts > miteinander zu tun. Na, irgendwo wirst du irgendwann mal einen Puffer zusammenstellen müssen, der die Daten zum Senden/Schreiben enthält. Denn alle solchen Transfers geschehen aus dem Speicher. Außer auf kleinen Mikrocontrollern ohne DMA, aber dort wird man meist eh auf Library-Routinen zurückgreifen, die am Ende auch wieder eine Adresse und Länge erwarten.
Andreas B. schrieb: > Jasch schrieb: >> Die In-Memory-Datenstrukturen und die übers Netzwerk verschickten oder >> auf die Platte geschriebenen Daten haben strukturell genau garnichts >> miteinander zu tun. > > Na, irgendwo wirst du irgendwann mal einen Puffer zusammenstellen > müssen, der die Daten zum Senden/Schreiben enthält. Denn alle solchen > Transfers geschehen aus dem Speicher. Außer auf kleinen Mikrocontrollern > ohne DMA, aber dort wird man meist eh auf Library-Routinen > zurückgreifen, die am Ende auch wieder eine Adresse und Länge erwarten. Dem stimme ich zu. ;-) Es las sich halt für mich als ob Du irgendwelche Structs direkt ins Netzwerk pusten wolltest.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.