In der Win32-API-Programmierung wird normalerweise C struct
s mit mehreren Feldern verwendet. Normalerweise haben nur einige von ihnen aussagekräftige Werte und alle anderen müssen auf Null gesetzt werden. Dies kann auf zwei Arten erreicht werden:
STRUCT theStruct;
memset( &theStruct, 0, sizeof( STRUCT ) );
oder
STRUCT theStruct = {};
Die zweite Variante sieht sauberer aus - sie ist ein Einzeiler, hat keine Parameter, die falsch eingegeben werden könnten und zu einem Fehler beim Pflanzen führen könnten.
Hat es irgendwelche Nachteile gegenüber der ersten Variante? Welche Variante und warum?
c++
c
visual-c++
struct
initialization
scharfer Zahn
quelle
quelle
Antworten:
Diese beiden Konstrukte haben eine sehr unterschiedliche Bedeutung. Die erste verwendet eine
memset
Funktion, mit der ein Speicherpuffer auf einen bestimmten Wert gesetzt werden soll . Der zweite, der ein Objekt initialisiert . Lassen Sie es mich mit ein bisschen Code erklären:Nehmen wir an, Sie haben eine Struktur, die nur Mitglieder von POD-Typen enthält ("Einfache alte Daten" - siehe Was sind POD-Typen in C ++? )
struct POD_OnlyStruct { int a; char b; }; POD_OnlyStruct t = {}; // OK POD_OnlyStruct t; memset(&t, 0, sizeof t); // OK as well
In diesem Fall macht das Schreiben eines
POD_OnlyStruct t = {}
oderPOD_OnlyStruct t; memset(&t, 0, sizeof t)
keinen großen Unterschied, da der einzige Unterschied, den wir hier haben, darin besteht, dass die Ausrichtungsbytes im Falle dermemset
Verwendung auf den Wert Null gesetzt werden. Da Sie normalerweise keinen Zugriff auf diese Bytes haben, gibt es für Sie keinen Unterschied.Da Sie Ihre Frage als C ++ markiert haben, versuchen wir ein anderes Beispiel mit anderen Mitgliedstypen als POD :
struct TestStruct { int a; std::string b; }; TestStruct t = {}; // OK { TestStruct t1; memset(&t1, 0, sizeof t1); // ruins member 'b' of our struct } // Application crashes here
In diesem Fall ist die Verwendung eines Ausdrucks wie
TestStruct t = {}
gut, und die Verwendung einesmemset
on führt zum Absturz. Folgendes passiert, wenn Sie Folgendes verwendenmemset
: Es wird ein Objekt vom TypTestStruct
erstellt, wodurch ein Objekt vom Typ erstellt wirdstd::string
, da es Mitglied unserer Struktur ist. Als nächstes wirdmemset
der Speicher, in dem sich das Objektb
befand, auf einen bestimmten Wert gesetzt, z. B. Null. Sobald unser TestStruct-Objekt den Gültigkeitsbereich verlässt, wird es zerstört und wenn der Zug zu seinem Mitglied kommtstd::string b
, wird ein Absturz angezeigt , da alle internen Strukturen dieses Objekts durch das zerstört wurdenmemset
.Die Realität ist also, dass diese Dinge sehr unterschiedlich sind , und obwohl Sie
memset
in bestimmten Fällen manchmal eine ganze Struktur auf Null setzen müssen, ist es immer wichtig sicherzustellen, dass Sie verstehen, was Sie tun, und keinen Fehler wie in unserem zweiten machen Beispiel.Meine Stimme - Einsatz
memset
auf Objekte nur , wenn es erforderlich ist, und verwenden Sie die Standard - Initialisierungx = {}
in allen anderen Fällen.quelle
std::string
.if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class subobject is zero-initialized and padding is initialized to zero bits;
Abhängig von den Strukturelementen sind die beiden Varianten nicht unbedingt gleichwertig.
memset
setzt die Struktur auf all-bits-zero, während die Wertinitialisierung alle Mitglieder auf den Wert null initialisiert. Der C-Standard garantiert, dass diese nur für integrale Typen gleich sind, nicht für Gleitkommawerte oder Zeiger.Außerdem erfordern einige APIs, dass die Struktur wirklich auf All-Bit-Null gesetzt wird. Beispielsweise verwendet die Berkeley-Socket-API Strukturen polymorph, und dort ist es wichtig, die gesamte Struktur wirklich auf Null zu setzen, nicht nur die Werte, die offensichtlich sind. In der API-Dokumentation sollte angegeben werden, ob die Struktur wirklich alle Bits Null sein muss, sie kann jedoch fehlerhaft sein.
Aber wenn keiner dieser oder ein ähnlicher Fall zutrifft, liegt es an Ihnen. Ich würde bei der Definition der Struktur eine Wertinitialisierung bevorzugen, da dies die Absicht klarer kommuniziert. Wenn Sie eine vorhandene Struktur
memset
auf Null setzen müssen, ist dies natürlich die einzige Wahl (abgesehen von der Initialisierung jedes Elements auf Null von Hand, aber dies würde normalerweise nicht durchgeführt, insbesondere bei großen Strukturen).quelle
Wenn Ihre Struktur Dinge enthält wie:
int a; char b; int c;
Dann werden Auffüllbytes zwischen "b" und "c" eingefügt. memset () setzt diese auf Null, die andere nicht, sodass 3 Byte Müll vorhanden sind (wenn Ihre Ints 32 Bit sind). Wenn Sie beabsichtigen, Ihre Struktur zum Lesen / Schreiben aus einer Datei zu verwenden, kann dies wichtig sein.
quelle
Ich würde die Wertinitialisierung verwenden, da sie sauber und weniger fehleranfällig aussieht, wie Sie erwähnt haben. Ich sehe keinen Nachteil darin.
Sie können sich jedoch darauf verlassen
memset
, die Struktur auf Null zu setzen, nachdem sie verwendet wurde.quelle
nicht, dass es üblich ist, aber ich denke, der zweite Weg hat auch den Vorteil, Floats auf Null zu initialisieren. Während ein Memset würde sicherlich nicht
quelle
while doing a memset would certainly not
- nicht ganz richtig. Bei x86- und x64-Mems wird ein Float / Double auf Null gesetzt und auf Null gesetzt. Sicher, dies ist nicht im C / C ++ - Standard enthalten, funktioniert aber auf den beliebtesten Plattformen.Die Wertinitialisierung, da sie zur Kompilierungszeit durchgeführt werden kann.
Außerdem initialisiert 0 alle POD-Typen korrekt.
Das memset () wird zur Laufzeit erstellt.
Auch die Verwendung von memset () ist verdächtig, wenn die Struktur nicht POD ist.
Initialisiert Nicht-Int-Typen nicht korrekt (auf Null).
quelle
In einigen Compilern
STRUCT theStruct = {};
würdememset( &theStruct, 0, sizeof( STRUCT ) );
in die ausführbare Datei übersetzt. Einige C-Funktionen sind bereits für die Laufzeiteinrichtung verknüpft, sodass der Compiler diese Bibliotheksfunktionen wie memset / memcpy zur Verfügung hat.quelle
struct something foo = { x, y, z }
und Cachegrind zeigte, dass 70% der "Arbeit" meines Programms ausgeführt wurde,memset
da die Strukturen bei JEDEM Funktionsaufruf auf Null gesetzt wurden.Wenn es viele Zeigerelemente gibt und Sie wahrscheinlich in Zukunft weitere hinzufügen werden, kann es hilfreich sein, memset zu verwenden. In Kombination mit geeigneten
assert(struct->member)
Aufrufen können Sie verhindern, dass zufällige Abstürze versuchen, einen fehlerhaften Zeiger zu erkennen, den Sie vergessen haben, zu initialisieren. Aber wenn Sie nicht so vergesslich sind wie ich, ist die Initialisierung der Mitglieder wahrscheinlich die beste!Allerdings , wenn Sie Ihre Struktur als Teil einer öffentlichen API verwendet wird, sollten Sie Client - Code zu verwenden Memset als Anforderung erhalten. Dies hilft bei der Zukunftssicherung, da Sie neue Mitglieder hinzufügen können und der Client-Code diese im Memset-Aufruf automatisch NULL macht, anstatt sie in einem (möglicherweise gefährlichen) nicht initialisierten Zustand zu belassen. Dies tun Sie beispielsweise, wenn Sie mit Socket-Strukturen arbeiten.
quelle
memset
mit der falschen Strukturgröße aufgerufen. Wenn der Clientcode neu kompiliert wird, muss er auf die aktualisierte Headerdatei mit der Strukturdefinition zugreifen, damit diememset
Initialisierung entweder oder der Wert funktioniert. (Der Client und die Bibliothek müssen jedoch eine konsistente Vorstellung davon haben, wie der Nullzeiger dargestellt wird. Wenn die API dies empfiehltmemset
, sollte sie gegen alle Bits Null und nicht gegen NULL