Dans un cours de programmation système que j’avais suivi au semestre précédent, nous devions implémenter un client/serveur de base en C. Lors de l’initialisation des structures, comme sock_addr_in
, ou des tampons de chars (que nous utilisions pour échanger des données entre et serveur), le professeur nous a demandé d’utiliser uniquement bzero
et non memset
pour les initialiser. Il n'a jamais expliqué pourquoi, et je suis curieux de savoir s'il existe une raison valable à cela.
Je vois ici: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown que bzero
est plus efficace du fait que c'est seulement La remise à zéro de la mémoire ne sera jamais nécessaire, donc aucune vérification supplémentaire que memset
ne peut faire. Cela ne semble toujours pas nécessairement être une raison pour ne pas utiliser absolument memset
pour mettre à zéro la mémoire.
bzero
est considéré comme obsolète et n'est en outre pas une fonction C standard. Selon le manuel, memset
est préférable à bzero
pour cette raison. Alors, pourquoi voudriez-vous toujours utiliser bzero
sur memset
? Juste pour les gains d'efficacité, ou est-ce quelque chose de plus? De même, quels sont les avantages de memset
par rapport à bzero
qui en font l’option privilégiée de facto pour les programmes plus récents?
Je ne vois aucune raison de préférer bzero
à memset
.
memset
est une fonction C standard tandis que bzero
n'a jamais été une fonction C standard. La raison en est probablement parce que vous pouvez obtenir exactement la même fonctionnalité en utilisant la fonction memset
.
Maintenant, en ce qui concerne l'efficacité, des compilateurs comme gcc
utilisent des implémentations intégrées pour memset
qui passent à une implémentation particulière lorsqu'une constante 0
est détectée. Idem pour glibc
lorsque les commandes intégrées sont désactivées.
Je suppose que vous avez utilisé (ou votre professeur a été influencé par) Programmation réseau UNIX par W. Richard Stevens. Il utilise bzero
fréquemment au lieu de memset
, même dans l'édition la plus récente. Le livre est si populaire que je pense que c'est devenu un idiome de la programmation réseau et c'est pourquoi vous le voyez toujours utilisé.
Je voudrais rester avec memset
simplement parce que bzero
est obsolète et réduit la portabilité. Je doute que l’utilisation de l’un par rapport à l’autre soit un réel avantage.
Le seul avantage que bzero()
a sur memset()
pour la mise à zéro de la mémoire est qu'il existe un risque réduit d'erreur.
Plus d'une fois, j'ai rencontré un bug qui ressemblait à:
memset(someobject, size_of_object, 0); // clear object
Le compilateur ne se plaindra pas (même si certains niveaux d’avertissement peuvent être augmentés) et l’effet sera que la mémoire ne sera pas effacée. Parce que cela ne détruit pas l'objet - il le laisse simplement tranquille - il y a une chance décente que le bogue ne se manifeste pas de manière évidente.
Le fait que bzero()
ne soit pas standard est un irritant mineur. (FWIW, je ne serais pas surpris si la plupart des appels de fonction dans mes programmes sont non standard; en fait, écrire de telles fonctions est un peu mon travail).
Dans un commentaire sur une autre réponse, Aaron Newton a cité ce qui suit dans Unix Network Programming, Volume 1, 3ème édition de Stevens et al., Section 1.2 (non souligné dans l'original):
bzero
n'est pas une fonction ANSI C. Il est dérivé du premier code de réseau Berkely. Néanmoins, nous l'utilisons dans tout le texte, au lieu de la fonction ANSI Cmemset
, parce quebzero
est plus facile à mémoriser (avec seulement deux arguments) quememset
(avec trois arguments). Presque tous les fournisseurs qui prennent en charge l’API sockets fournissent égalementbzero
, et si ce n’est pas le cas, nous fournissons une définition de macro dans notre en-têteunp.h
.En effet, l'auteur de TCPv3 [TCP/IP Illustrated, Volume 3 - Stevens 1996] a commis l'erreur de permuter les deuxième et troisième arguments en
memset
dans 10 occurrences lors de la première impression. Un compilateur C ne peut pas intercepter cette erreur car les deux arguments sont du même type. (En fait, le deuxième argument est unint
et le troisième argument est unsize_t
, qui est généralement ununsigned int
, mais les valeurs spécifiées, 0 et 16, sont toujours acceptables pour autre type d’argument.) L’appel àmemset
fonctionnait toujours, car seules quelques fonctions de socket exigent en réalité que les 8 derniers octets d’une structure d’adresse de socket Internet soient mis à 0. Néanmoins, il s’agissait d’une erreur, et une solution qui pourrait être évitée en utilisantbzero
, car le remplacement des deux arguments parbzero
sera toujours capturé par le compilateur C si des prototypes de fonctions sont utilisés.
Je pense également que la grande majorité des appels à memset()
vont à zéro, alors pourquoi ne pas utiliser une API adaptée à ce cas d'utilisation?
Un inconvénient possible de bzero()
est que les compilateurs sont plus susceptibles d’optimiser memcpy()
car c’est standard et qu’ils peuvent donc être écrits pour le reconnaître. Cependant, gardez à l'esprit qu'un code correct est toujours préférable à un code incorrect optimisé. Dans la plupart des cas, utiliser bzero()
n'aura pas d'incidence notable sur les performances de votre programme, et bzero()
peut être une macro ou une fonction en ligne qui se développe en memcpy()
.
En bref: memset
nécessite plus d'opérations d'assemblage que bzero
.
Ceci est la source: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown
Je voulais mentionner quelque chose à propos de l'argument bzero vs memset. Installez ltrace puis comparez ce qu’il fait sous le capot. Sous Linux avec libc6 (2.19-0ubuntu6.6), les appels effectués sont exactement les mêmes (via ltrace ./test123
):
long m[] = {0}; // generates a call to memset(0x7fffefa28238, '\0', 8)
int* p;
bzero(&p, 4); // generates a call to memset(0x7fffefa28230, '\0', 4)
On m'a dit que si je ne travaillais pas dans le entrailles profondes de la libc ou n'importe quel nombre d'interfaces noyau/syscall, je n'ai pas à m'en soucier. Tout ce qui me préoccupe, c'est que l'appel réponde à l'exigence de mise à zéro du tampon. D'autres ont mentionné lequel est préférable à l'autre, alors je vais m'arrêter ici.
Vous avez probablement ne devriez pas utiliser bzero
, ce n'est pas vraiment du C standard, c'est un truc POSIX.
Et notez que Word "était" - il était obsolète dans POSIX.1-2001 et supprimé dans POSIX.1-2008 par respect pour memset, vous feriez donc mieux d'utiliser la fonction C standard.
Avez-vous comme vous voulez. :-)
#ifndef bzero
#define bzero(d,n) memset((d),0,(n))
#endif
Notez que:
bzero
ne renvoie rien, memset
renvoie le pointeur vide (d
). Cela peut être corrigé en ajoutant le transtypage à annuler dans la définition.#ifndef bzero
ne vous empêche pas de cacher la fonction d'origine même si elle existe. Il teste l'existence d'une macro. Cela peut causer beaucoup de confusion.bzero
via des pointeurs de fonction, cela ne fonctionnera pas.Pour la fonction memset, le deuxième argument est un int
et le troisième argument est size_t
,
void *memset(void *s, int c, size_t n);
qui est typiquement un unsigned int
, mais si les valeurs telles que, 0 and 16
pour les deuxième et troisième arguments sont respectivement entrées dans le mauvais ordre (16 et 0), un tel appel à memset peut toujours fonctionner, mais fera rien. Parce que le nombre d'octets à initialiser est spécifié sous la forme 0
.
void bzero(void *s, size_t n)
Une telle erreur peut être évitée en utilisant bzero, car l’échange des deux arguments en bzero sera toujours capturé par le compilateur C si des prototypes de fonctions sont utilisés.
memset prend 3 paramètres, bzero en prend 2 en mémoire, ce paramètre supplémentaire prend 4 octets de plus et la plupart du temps, il sera utilisé pour tout mettre à 0