web-dev-qa-db-fra.com

memset () ou initialisation de la valeur pour remettre à zéro une structure?

Dans la programmation de l'API Win32, il est typique d'utiliser C structs avec plusieurs champs. Habituellement, seuls quelques-uns d'entre eux ont des valeurs significatives et tous les autres doivent être mis à zéro. Cela peut être réalisé de l'une des deux manières suivantes:

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

ou

STRUCT theStruct = {};

La deuxième variante a l'air plus propre - c'est une doublure, elle n'a pas de paramètres qui pourraient être mal tapés et conduire à une erreur de plantation.

At-il des inconvénients par rapport à la première variante? Quelle variante utiliser et pourquoi?

61
sharptooth

Ces deux constructions ont un très différent dans leur signification. Le premier utilise une fonction memset, qui est destinée à définir un tampon de mémoire à une certaine valeur. Le second à initialiser un objet. Permettez-moi de l'expliquer avec un peu de code:

Supposons que vous ayez une structure qui a des membres niquement des types POD

struct POD_OnlyStruct
{
    int a;
    char b;
};

POD_OnlyStruct t = {};  // OK

POD_OnlyStruct t;
memset(&t, 0, sizeof t);  // OK as well

Dans ce cas, l'écriture d'un POD_OnlyStruct t = {} Ou POD_OnlyStruct t; memset(&t, 0, sizeof t) ne fait pas beaucoup de différence, car la seule différence que nous avons ici est le alignement octets étant mis à zéro en cas d'utilisation de memset. Puisque vous n'avez pas accès à ces octets normalement, il n'y a aucune différence pour vous.

D'un autre côté, puisque vous avez marqué votre question en C++, essayons un autre exemple, avec le membre types différents de 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

Dans ce cas, l'utilisation d'une expression comme TestStruct t = {} Est bonne, et l'utilisation d'un memset dessus entraînera un plantage. Voici ce qui se passe si vous utilisez memset - un objet de type TestStruct est créé, créant ainsi un objet de type std::string, Car il est membre de notre structure. Ensuite, memset définit la mémoire où l'objet b était situé à une certaine valeur, disons zéro. Maintenant, une fois que notre objet TestStruct sera hors de portée, il sera détruit et lorsque le tour arrivera à son membre std::string b Vous verrez un plantage, car toutes les structures internes de cet objet ont été ruinées par le memset.

Donc, la réalité est, ces choses sont très différentes, et bien que vous ayez parfois besoin de memset toute une structure en zéros dans certains cas, il est toujours important de vous assurer de bien comprendre ce que vous fais, et ne commets pas d'erreur comme dans notre deuxième exemple.

Mon vote - utilisez memset sur les objets niquement si cela est nécessaire, et utilisez l'initialisation par défautx = {} Dans tous les autres cas.

84
Dmitry

Selon les membres de la structure, les deux variantes ne sont pas nécessairement équivalentes. memset définira la structure sur all-bits-zero tandis que l'initialisation de la valeur initialisera tous les membres à la valeur zéro. La norme C garantit qu'elles ne sont identiques que pour les types intégraux, et non pour les valeurs à virgule flottante ou les pointeurs.

De plus, certaines API nécessitent que la structure soit réellement définie sur tous les bits à zéro. Par exemple, l'API socket Berkeley utilise des structures de manière polymorphe, et il est important de vraiment mettre la structure entière à zéro, pas seulement les valeurs qui sont apparentes. La documentation de l'API doit indiquer si la structure doit vraiment être tout à zéro, mais elle peut être déficiente.

Mais si ni l'un ni l'autre, ou un cas similaire, ne s'applique, alors c'est à vous de décider. Je voudrais, lors de la définition de la structure, préférer l'initialisation de la valeur, car cela communique plus clairement l'intention. Bien sûr, si vous devez mettre à zéro une structure existante, memset est le seul choix (enfin, à part initialiser chaque membre à zéro à la main, mais cela ne serait normalement pas fait, en particulier pour les grandes structures).

29
JaakkoK

Si votre struct contient des choses comme:

int a;
char b;
int c;

Ensuite, des octets de remplissage seront insérés entre "b" et "c". memset () mettra à zéro ceux-ci, dans l'autre sens non, il y aura donc 3 octets de déchets (si vos entrées sont de 32 bits). Si vous avez l'intention d'utiliser votre structure pour lire/écrire à partir d'un fichier, cela peut être important.

10
peufeu

J'utiliserais l'initialisation de la valeur car elle semble propre et moins sujette aux erreurs comme vous l'avez mentionné. Je ne vois aucun inconvénient à le faire.

Vous pouvez vous fier à memset pour mettre à zéro la structure une fois qu'elle a été utilisée.

7
Gregory Pakosz

pas que ce soit commun, mais je suppose que la deuxième façon a également l'avantage d'initialiser les flotteurs à zéro. Tout en faisant un memset ne serait certainement pas

5
Toad

Dans certains compilateurs, STRUCT theStruct = {}; Se traduirait par memset( &theStruct, 0, sizeof( STRUCT ) ); dans l'exécutable. Certaines fonctions C sont déjà liées pour effectuer la configuration d'exécution, de sorte que le compilateur dispose de ces fonctions de bibliothèque comme memset/memcpy.

3
Gerhard

L'initialisation de la valeur car elle peut être effectuée au moment de la compilation.
De plus, il initialise correctement tous les types de POD.

Le memset () est exécuté lors de l'exécution.
L'utilisation de memset () est également suspecte si la structure n'est pas POD.
N'initialise pas correctement (à zéro) les types non int.

2
Martin York