memset() o inizializzazione del valore per azzerare una struttura?

memset() o inizializzazione del valore per azzerare una struttura?


Nella programmazione API Win32 è tipico usare C struct s con più campi. Di solito solo un paio di loro hanno valori significativi e tutti gli altri devono essere azzerati. Questo può essere ottenuto in uno dei due modi:


STRUCT theStruct;
memset( &theStruct, 0, sizeof( STRUCT ) );

o


STRUCT theStruct = {};

La seconda variante ha un aspetto più pulito:è one-liner, non ha parametri che potrebbero essere digitati in modo errato e causare un errore di inserimento.


Ha degli inconvenienti rispetto alla prima variante? Quale variante utilizzare e perché?


Risposte:


Questi due costrutti sono un molto diversi nel loro significato. Il primo usa un memset funzione, che ha lo scopo di impostare un buffer di memoria su un determinato valore . Il secondo per inizializzare un oggetto . Lascia che te lo spieghi con un po' di codice:


Si supponga di avere una struttura con membri solo di tipi POD ("Dati vecchi semplici" - vedi Cosa sono i tipi POD 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 questo caso scrivendo un POD_OnlyStruct t = {} o POD_OnlyStruct t; memset(&t, 0, sizeof t) non fa molta differenza, poiché l'unica differenza che abbiamo qui è l'allineamento byte impostati su valore zero in caso di memset Usato. Dal momento che normalmente non hai accesso a quei byte, non c'è differenza per te.


D'altra parte, dato che hai contrassegnato la tua domanda come C++, proviamo un altro esempio, con tipi di membri diversi da 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 questo caso usando un'espressione come TestStruct t = {} è buono e usando un memset su di esso porterà al crash. Ecco cosa succede se usi memset - un oggetto di tipo TestStruct viene creato, creando così un oggetto di tipo std::string , poiché è un membro della nostra struttura. Successivamente, memset imposta la memoria in cui l'oggetto b era localizzato a un certo valore, diciamo zero. Ora, una volta che il nostro oggetto TestStruct esce dall'ambito, verrà distrutto e quando arriverà il turno del suo membro std::string b vedrai un arresto anomalo, poiché tutte le strutture interne di quell'oggetto sono state rovinate dal memset .


Quindi, la realtà è che quelle cose sono molto diverse e anche se a volte è necessario memset un'intera struttura a zero in certi casi, è sempre importante assicurarsi di capire cosa si sta facendo e non commettere errori come nel nostro secondo esempio.


Il mio voto:usa memset sugli oggetti solo se è richiesto, e usa il predefinito inizializzazione x = {} in tutti gli altri casi.


Alcune risposte al codice


STRUCT theStruct;
memset( &theStruct, 0, sizeof( STRUCT ) );
STRUCT theStruct = {};
struct POD_OnlyStruct {
int a;
char b;
};
POD_OnlyStruct t = {};
// OK POD_OnlyStruct t;
memset(&t, 0, sizeof t);
// OK as well
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
int a;
char b;
int c;