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.
!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
myStructeinsDavon={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...
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:
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.