102 votes

Memset() ou initialisation de la valeur à zéro d'une structure?

En programmation API Win32, il est typique d'utiliser des structs C avec plusieurs champs. En général, seulement quelques-uns ont des valeurs significatives et tous les autres doivent être mis à zéro. Cela peut être accompli de l'une des deux manières :

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

ou

STRUCT theStruct = {};

La deuxième variante semble plus propre - c'est une seule ligne, elle n'a pas de paramètres qui pourraient être mal tapés et entraîner une erreur.

Est-ce qu'elle présente des inconvénients par rapport à la première variante ? Quelle variante utiliser et pourquoi ?

0 votes

Cette Comment répondre[1] à une question ultérieure semble être plus utile et plus facile. [1]: stackoverflow.com/questions/4625212/class-initialization-list/‌​t/…

119voto

Dmitry Points 3946

Ces deux constructions sont très différentes dans leur signification. Le premier utilise une fonction memset, qui est destinée à définir un tampon mémoire sur une certaine valeur. Le deuxième pour initialiser un objet. Laissez-moi vous expliquer cela avec un peu de code :

Supposons que vous avez une structure qui a des membres uniquement de types POD ("Plain Old Data" - voir Quels sont les types POD en C++?)

struct StructurePOD_Seul
{
    int a;
    char b;
};

StructurePOD_Seul t = {};  // OK

StructurePOD_Seul t;
memset(&t, 0, sizeof t);  // OK aussi

Dans ce cas, écrire un StructurePOD_Seul t = {} ou StructurePOD_Seul t; memset(&t, 0, sizeof t) ne fait pas beaucoup de différence, car la seule différence que nous avons ici est que les octets d'alignement sont définis sur une valeur zéro en cas d'utilisation de memset. Comme vous n'avez normalement pas accès à ces octets, il n'y a pas de différence pour vous.

D'un autre côté, comme vous avez étiqueté votre question comme étant en C++, essayons un autre exemple, avec des types de membres différents des POD:

struct StructureTest
{
    int a;
    std::string b;
};

StructureTest t = {};  // OK

{
    StructureTest t1;
    memset(&t1, 0, sizeof t1);  // ruine le membre 'b' de notre structure
}  // L'application plante ici

Dans ce cas, utiliser une expression comme StructureTest t = {} est bien, et utiliser un memset sur cela entraînera un crash. Voici ce qui se passe si vous utilisez memset - un objet de type StructureTest est créé, créant ainsi un objet de type std::string, puisque c'est un membre de notre structure. Ensuite, memset définit la mémoire où l'objet b était situé sur une certaine valeur, disons zéro. Maintenant, une fois que notre objet StructureTest sort du contexte, il sera détruit et lorsque viendra le tour de son membre std::string b, vous verrez un crash, car toutes les structures internes de cet objet ont été ruinées par le memset.

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

Mon conseil - utiliser memset sur des objets uniquement si c'est nécessaire, et utiliser l'initialisation par défaut x = {} dans tous les autres cas.

0 votes

Salut Dimity! J'ai une structure qui a quelques membres et j'ai essayé la première option de memsetting: "struct stVar={}". Mais je reçois un avertissement "-Wmissing-field-initializers". Est-ce un problème?

2 votes

Dans ce cas, par POD, voulez-vous réellement dire un objet facilement constructible (c'est-à-dire un objet sans de constructeur fourni par l'utilisateur) ? Je ne pense pas que cela devrait être limité à POD.

0 votes

Ceci ne plantera pas : coliru.stacked-crooked.com/a/4b3dbf0b8761bc9b Techniquement, c'est un comportement indéfini car la structure n'est pas trivialement assignable (d'où le avertissement du compilateur). Cependant, je doute qu'il y ait une plateforme commune où des octets mis à zéro soient une valeur invalide pour std::string.

42voto

jk. Points 5780

En fonction des membres de la structure, les deux variantes ne sont pas nécessairement équivalentes. memset va définir la structure à tous les bits à zéro alors que l'initialisation de la valeur va initialiser tous les membres à la valeur zéro. La norme C garantit que ces deux méthodes sont les mêmes uniquement pour les types entiers, pas pour les valeurs à virgule flottante ou les pointeurs.

De plus, certaines API nécessitent que la structure soit réellement définie à tous les bits à zéro. Par exemple, l'API des sockets Berkeley utilise des structures de manière polymorphe, et il est important de vraiment définir toute la structure à zéro, pas seulement les valeurs apparentes. La documentation de l'API devrait préciser si la structure doit vraiment être définie à tous les bits à zéro, mais cela pourrait être insuffisant.

Mais si aucune de ces situations, ou un cas similaire, ne s'applique, alors c'est à vous de décider. Lorsque vous définissez la structure, je préfère l'initialisation de la valeur, car cela communique l'intention de manière plus claire. Bien sûr, si vous avez besoin de mettre à zéro une structure existante, memset est le seul choix (à part initialiser manuellement chaque membre à zéro, mais cela ne serait généralement pas fait, surtout pour de grandes structures).

0 votes

Par curiosité, sur quelle plateforme un flottant avec tous les bits à zéro n'est pas le zéro positif?

3 votes

Plusieurs anciens processeurs pré-IEEE-754 avaient des zéros flottants étranges. Les calculs non-754 pourraient revenir un jour, on ne sait jamais, il vaut mieux ne pas écrire ces bugs.

1 votes

Peu importe. La norme C ne spécifie pas le format de nombre flottant utilisé. Donc même si cela fonctionne actuellement pour IEEE 754, cela pourrait ne pas fonctionner sur une implémentation de nombre flottant différente (future ou passée)

14voto

peufeu Points 4758

Si votre structure contient des éléments tels que :

int a;
char b;
int c;

Alors des octets de remplissage seront insérés entre b et c. memset les mettra à zéro, l'autre manière ne le fera pas, donc il y aura 3 octets de données non désirées (si vos entiers font 32 bits). Si vous avez l'intention d'utiliser votre structure pour lire/écrire dans un fichier, cela pourrait être important.

3 votes

Il semble que ce ne soit pas vrai. De CppReference : "Si T est un type de classe non union, toutes les classes de base et les membres de données non statiques sont initialisés à zéro, et tous les espaces de remplissage sont initialisés à zéro bits. Les constructeurs, le cas échéant, sont ignorés." fr.cppreference.com/w/cpp/language/zero_initialization

1 votes

Probablement s'applique seulement à C et non à C++.

9voto

Gregory Pakosz Points 35546

Je préférerais utiliser l'initialisation de valeur car cela paraît propre et moins sujet aux erreurs comme vous l'avez mentionné. Je ne vois aucun inconvénient à le faire.

Vous pourriez utiliser memset pour mettre à zéro la structure après son utilisation cependant.

0 votes

L'inconvénient est de devoir se souvenir d'ajouter un autre zéro à chaque fois que vous ajoutez un nouveau membre.

6voto

Toad Points 7868

Pas que ce soit courant, mais je suppose que la deuxième méthode a également l'avantage d'initialiser les flottants à zéro, alors qu'un memset ne le ferait certainement pas.

0 votes

lorsque vous effectuez un memset ne le ferait certainement pas - pas entièrement vrai. En fait, sur les plateformes x86 et x64, le fait de placer un flottant/double à zéro le mettra bien à zéro. Bien sûr, cela n'est pas conforme à la norme C/C++, mais cela fonctionne sur la plupart des plateformes populaires.

3 votes

Sbk: pour l'instant... qui sait quelle implémentation de nombre flottant ils pourraient commencer à utiliser. IEEE 754 n'est pas défini pour le compilateur. Donc même si cela pourrait fonctionner maintenant, c'est juste de la chance pour vous, mais cela peut poser des problèmes plus tard.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X