web-dev-qa-db-fra.com

Pourquoi utiliser bzero over memset?

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?

147
PseudoPsyche

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.

139
ouah

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.

63
austin

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 C memset, parce que bzero est plus facile à mémoriser (avec seulement deux arguments) que memset (avec trois arguments). Presque tous les fournisseurs qui prennent en charge l’API sockets fournissent également bzero, et si ce n’est pas le cas, nous fournissons une définition de macro dans notre en-tête unp.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 un int et le troisième argument est un size_t, qui est généralement un unsigned 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 utilisant bzero, car le remplacement des deux arguments par bzero 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().

46
Michael Burr

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

4
Tal Bar

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.

3
gumchew

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.

3
paxdiablo

Avez-vous comme vous voulez. :-)

#ifndef bzero
#define bzero(d,n) memset((d),0,(n))
#endif

Notez que:

  1. L'original bzero ne renvoie rien, memset renvoie le pointeur vide (d). Cela peut être corrigé en ajoutant le transtypage à annuler dans la définition.
  2. #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.
  3. Il est impossible de créer un pointeur de fonction sur une macro. Lorsque vous utilisez bzero via des pointeurs de fonction, cela ne fonctionnera pas.
2
Bruce

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.

1
havish

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

0
Skynight