it-swarm.com.de

Warum bzero over memset verwenden?

In einer Systemprogrammierstunde, die ich in diesem Semester absolviert habe, mussten wir einen grundlegenden Client/Server in C implementieren. Bei der Initialisierung der Strukturen wie sock_addr_in oder Zeichenpuffer (die wir zum Hin- und Herschicken von Daten zwischen Client und Server verwendet haben) Der Professor hat uns angewiesen, sie nur mit bzero und nicht mit memset zu initialisieren. Er hat nie erklärt warum, und ich bin neugierig, ob es einen triftigen Grund dafür gibt?

Ich sehe hier: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown dass bzero aufgrund der Tatsache effizienter ist Es wird immer nur der Speicher auf Null gesetzt, es muss also keine zusätzliche Überprüfung durchgeführt werden, die memset durchführen kann. Das scheint aber immer noch kein Grund zu sein, memset nicht zum Nullsetzen des Speichers zu verwenden.

bzero gilt als veraltet und ist außerdem keine Standard-C-Funktion. Laut Handbuch wird memset aus diesem Grund bzero vorgezogen. Warum sollten Sie bzero weiterhin über memset verwenden? Nur für die Effizienzgewinne, oder ist es etwas mehr? Welche Vorteile hat memset gegenüber bzero, sodass es de facto die bevorzugte Option für neuere Programme ist?

147
PseudoPsyche

Ich sehe keinen Grund, bzero gegenüber memset vorzuziehen.

memset ist eine Standard-C-Funktion, während bzero noch nie eine C-Standardfunktion war. Das liegt wahrscheinlich daran, dass Sie mit der Funktion memset genau dieselbe Funktionalität erzielen können.

Im Hinblick auf die Effizienz verwenden Compiler wie gcc integrierte Implementierungen für memset, die zu einer bestimmten Implementierung wechseln, wenn eine Konstante 0 ist angeschlossen. Dasselbe gilt für glibc, wenn die integrierten Funktionen deaktiviert sind.

139
ouah

Ich nehme an, Sie haben (oder Ihr Lehrer wurde beeinflusst von) NIX Network Programming von W. Richard Stevens. Er verwendet bzero häufig anstelle von memset, auch in der aktuellsten Ausgabe. Das Buch ist so beliebt, ich denke, es ist zu einer Redewendung in der Netzwerkprogrammierung geworden, weshalb man es immer noch als gebraucht ansieht.

Ich würde einfach bei memset bleiben, weil bzero veraltet ist und die Portabilität verringert. Ich bezweifle, dass Sie echte Vorteile sehen würden, wenn Sie einen über den anderen einsetzen.

63
austin

Der einzige Vorteil, den bzero() meiner Meinung nach gegenüber memset() hat, um den Speicher auf Null zu setzen, ist die geringere Wahrscheinlichkeit, dass ein Fehler gemacht wird.

Mehr als einmal bin ich auf einen Fehler gestoßen, der so aussah:

memset(someobject, size_of_object, 0);    // clear object

Der Compiler wird sich nicht beschweren (obwohl einige Compiler möglicherweise einige Warnstufen erhöhen), und der Effekt wird sein, dass der Speicher nicht gelöscht wird. Da dies das Objekt nicht in den Papierkorb wirft - es lässt es einfach in Ruhe - besteht eine gute Chance, dass sich der Fehler nicht in etwas Offensichtlichem manifestiert.

Die Tatsache, dass bzero() nicht Standard ist, ist ein geringfügiger Reiz. (FWIW, ich würde mich nicht wundern, wenn die meisten Funktionsaufrufe in meinen Programmen nicht dem Standard entsprechen. Tatsächlich ist das Schreiben solcher Funktionen meine Aufgabe.).

In einem Kommentar zu einer anderen Antwort zitierte Aaron Newton Folgendes aus Unix Network Programming, Band 1, 3. Auflage von Stevens et al., Abschnitt 1.2 (Hervorhebung hinzugefügt):

bzero ist keine ANSI C-Funktion. Es ist aus dem frühen Berkely-Netzwerkcode abgeleitet. Trotzdem verwenden wir es im gesamten Text anstelle der ANSI-Funktion C memset, da bzero (mit nur zwei Argumenten) leichter zu merken ist als memset (mit drei Argumenten) ). Nahezu jeder Anbieter, der die Sockets-API unterstützt, bietet auch bzero an. Andernfalls wird in der Kopfzeile von unp.h Eine Makrodefinition angegeben.

In der Tat hat der Autor von TCPv3 [TCP/IP Illustrated, Band 3 - Stevens 1996] den Fehler begangen, das zweite und dritte Argument in 10 Fällen im ersten Ausdruck gegen memset zu tauschen. Ein C-Compiler kann diesen Fehler nicht abfangen, da beide Argumente vom gleichen Typ sind. (Tatsächlich ist das zweite Argument ein int und das dritte Argument ist size_t, Was normalerweise ein unsigned int Ist, aber die angegebenen Werte 0 und 16 sind immer noch akzeptabel für die andere Art von Argument.) Der Aufruf von memset funktionierte immer noch, da nur einige der Socket-Funktionen tatsächlich erfordern, dass die letzten 8 Bytes einer Internet-Socket-Adressstruktur auf 0 gesetzt werden war ein Fehler, der durch die Verwendung von bzero vermieden werden konnte, da das Austauschen der beiden Argumente in bzero immer vom C-Compiler abgefangen wird, wenn Funktionsprototypen verwendet werden.

Ich glaube auch, dass die überwiegende Mehrheit der Aufrufe von memset() sich auf null Arbeitsspeicher bezieht. Warum also nicht eine API verwenden, die auf diesen Anwendungsfall zugeschnitten ist?

Ein möglicher Nachteil von bzero() ist, dass Compiler mit größerer Wahrscheinlichkeit memcpy() optimieren, weil es Standard ist, und daher möglicherweise so geschrieben sind, dass sie es erkennen. Beachten Sie jedoch, dass der richtige Code immer noch besser ist als der falsche Code, der optimiert wurde. In den meisten Fällen hat die Verwendung von bzero() keine nennenswerten Auswirkungen auf die Leistung Ihres Programms, und bzero() kann eine Makro- oder Inline-Funktion sein, die zu memcpy() erweitert wird.

46
Michael Burr

Kurz gesagt: memset erfordert mehr Assembly-Operationen als bzero.

Dies ist die Quelle: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown

4
Tal Bar

Ich wollte etwas über das Argument bzero vs. memset erwähnen. Installieren Sie ltrace und vergleichen Sie dann, was es unter der Haube tut. Unter Linux mit libc6 (2.19-0ubuntu6.6) sind die getätigten Aufrufe genau gleich (über 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)

Mir wurde gesagt, es sei denn, ich arbeite in der tiefe Eingeweide von libc oder eine beliebige Anzahl von Kernel/Syscall-Schnittstelle, ich muss nicht über sie sorgen. Ich sollte mir nur Sorgen machen, dass der Anruf die Anforderung erfüllt, den Puffer auf Null zu setzen. Andere haben erwähnt, welches dem anderen vorzuziehen ist, also höre ich hier auf.

3
gumchew

Wahrscheinlich sollte nichtbzero verwenden, es ist eigentlich kein Standard-C, es war eine POSIX-Sache.

Und beachten Sie, dass Word "war" - es war veraltet in POSIX.1-2001 und entfernt in POSIX.1-2008 aus Rücksicht auf memset, damit Sie besser dran sind die Standard-C-Funktion.

3
paxdiablo

Haben Sie es wie Sie möchten. :-)

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

Beachten Sie, dass:

  1. Das ursprüngliche bzero gibt nichts zurück, memset gibt den ungültigen Zeiger (d) zurück. Dies kann behoben werden, indem der Typecast in der Definition als ungültig hinzugefügt wird.
  2. #ifndef bzero hindert Sie nicht daran, die ursprüngliche Funktion auszublenden, auch wenn sie vorhanden ist. Es testet die Existenz eines Makros. Dies kann viel Verwirrung stiften.
  3. Es ist unmöglich, einen Funktionszeiger auf ein Makro zu erstellen. Wenn Sie bzero über Funktionszeiger verwenden, funktioniert dies nicht.
2
Bruce

Für die Memset-Funktion ist das zweite Argument ein int und das dritte Argument ist size_t.

void *memset(void *s, int c, size_t n);

das ist normalerweise ein unsigned int, aber wenn die Werte wie 0 and 16 für das zweite und dritte Argument in der falschen Reihenfolge als 16 und 0 eingegeben werden, kann ein solcher Aufruf von memset immer noch funktionieren, aber werde nichts tun. Weil die Anzahl der zu initialisierenden Bytes mit 0 Angegeben ist.

void bzero(void *s, size_t n)

Ein solcher Fehler kann durch Verwendung von bzero vermieden werden, da das Austauschen der beiden Argumente in bzero immer vom C-Compiler abgefangen wird, wenn Funktionsprototypen verwendet werden.

1
havish

memset benötigt 3 Parameter, bzero benötigt 2 im Speicher, was bedeutet, dass zusätzliche Parameter 4 Bytes mehr benötigen und die meiste Zeit verwendet werden, um alles auf 0 zu setzen

0
Skynight