Warum bzero over memset verwenden?

156

Programmierung Klasse in einem System Ich habe diesen Vorsemesters, wir hatten einen einfaches Client / Server in C zu implementieren , wenn dem structs Initialisierung wie sock_addr_inoder char - Puffers (die wir verwenden , um Daten zurück zu senden und her zwischen Client und Server) der Professor hat uns angewiesen, sie nur zu verwenden bzeround nicht memsetzu initialisieren. Er hat nie erklärt warum und ich bin gespannt, ob es dafür einen triftigen Grund gibt?

Ich sehe hier: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown , bzerodas effizienter ist, weil immer nur Speicher auf Null gesetzt wird, also nicht müssen zusätzliche Überprüfungen durchführen, memsetdie dies möglicherweise tun. Das scheint jedoch nicht unbedingt ein Grund zu sein, es absolut nicht memsetzum Nullstellen des Speichers zu verwenden.

bzerogilt als veraltet und ist darüber hinaus keine Standard-C-Funktion. Laut Handbuch memsetwird bzeroaus diesem Grund bevorzugt . Warum sollten Sie also immer noch bzeroüber verwenden memset? Nur für die Effizienzgewinne, oder ist es etwas mehr? Was sind die Vorteile von memsetOver bzero, die es zur de facto bevorzugten Option für neuere Programme machen?

PseudoPsyche
quelle
28
"Warum bzero über memset verwenden?" - Nicht. Memset ist Standard, Bzero nicht.
30
bzero ist ein BSDism (). memset () ist ansi-c. Heutzutage wird bzero () wahrscheinlich als Makro implementiert. Bitten Sie Ihren Professor, sich zu rasieren und einige Bücher zu lesen. Effizienz ist ein Scheinargument. Ein Syscall oder Kontextwechsel kann leicht Zehntausende Taktticks kosten, ein Durchgang über einen Puffer läuft mit Busgeschwindigkeit. Wenn Sie Netzwerkprogramme optimieren möchten: Minimieren Sie die Anzahl der Systemaufrufe (durch Lesen / Schreiben größerer
Blöcke
7
Die Idee, die memsetmöglicherweise etwas weniger effizient ist, weil "ein bisschen mehr überprüft wird", ist definitiv ein Fall vorzeitiger Optimierung: Was auch immer die Vorteile sind, die Sie durch das Weglassen eines oder zweier CPU-Befehle sehen, es lohnt sich nicht, wenn Sie die Portabilität Ihrer CPU gefährden können Code. bzeroist veraltet, und das ist Grund genug, es nicht zu benutzen.
dasblinkenlight
4
Oft können Sie stattdessen einen Initialisierer `= {0}` hinzufügen und überhaupt keine Funktion aufrufen. Dies wurde einfacher, als um die Jahrhundertwende C aufhörte, lokale Variablen im Voraus zu deklarieren. Einige wirklich alte Papierwaren stecken jedoch noch tief im vorigen Jahrhundert fest.
MSalters
1
@SSAnne nein, aber es stammt höchstwahrscheinlich aus einem empfohlenen Buch für den Kurs, von dem er beeinflusst wurde, wie in einer der folgenden Antworten erwähnt: stackoverflow.com/a/17097072/1428743
PseudoPsyche

Antworten:

152

Ich sehe keinen Grund zu bevorzugen bzeroüber memset.

memsetist eine Standard-C-Funktion, war aber bzeronoch nie eine C-Standardfunktion. Das Grundprinzip ist wahrscheinlich, dass Sie mit der Funktion genau die gleiche Funktionalität erreichen memsetkönnen.

In Bezug auf die Effizienz verwenden Compiler beispielsweise gccintegrierte Implementierungen, für memsetdie zu einer bestimmten Implementierung gewechselt wird , wenn eine Konstante 0erkannt wird. Gleiches gilt, glibcwenn eingebaute Geräte deaktiviert sind.

ouah
quelle
Vielen Dank. Das macht Sinn. Ich war mir ziemlich sicher, dass memsetdies in diesem Fall immer verwendet werden sollte, war aber verwirrt darüber, warum wir es nicht verwendeten. Vielen Dank für die Klärung und Bestätigung meiner Gedanken.
PseudoPsyche
1
Ich hatte viele Probleme mit defekten bzeroImplementierungen. Bei nicht ausgerichteten Arrays wurde die angegebene Länge überschritten und ein wenig mehr Bytes auf Null gesetzt. Hatte noch nie ein solches Problem nach dem Wechsel zu memset.
Rustyx
Vergessen Sie nicht, memset_swelche Option verwendet werden soll, wenn Sie sicherstellen möchten, dass der Compiler einen Aufruf zum "Scrubben" des Speichers für sicherheitsrelevante Zwecke nicht stillschweigend optimiert (z. B. zum Löschen eines Speicherbereichs, der einen vertraulichen Speicher enthält) Informationen wie ein Klartext-Passwort).
Christopher Schultz
69

Ich vermute, Sie haben die UNIX-Netzwerkprogrammierung von W. Richard Stevens verwendet (oder Ihr Lehrer wurde von ihr beeinflusst) . Er verwendet bzerohäufig statt memset, auch in der aktuellsten Ausgabe. Das Buch ist so beliebt, dass es meiner Meinung nach zu einer Redewendung in der Netzwerkprogrammierung geworden ist, weshalb es immer noch verwendet wird.

Ich würde memseteinfach dabei bleiben, weil bzeroes veraltet ist und die Portabilität verringert. Ich bezweifle, dass Sie echte Vorteile sehen würden, wenn Sie einen über den anderen verwenden.

Austin
quelle
4
Du wärst richtig. Wir hatten keine Lehrbücher für diesen Kurs benötigt, aber ich habe den Lehrplan noch einmal überprüft und UNIX Network Programming ist in der Tat als optionale Ressource aufgeführt. Vielen Dank.
PseudoPsyche
9
Es ist tatsächlich schlimmer als das. Es wurde in POSIX.1-2001 veraltet und in POSIX.1-2008 entfernt .
Paxdiablo
9
Zitieren von Seite 8 der dritten Ausgabe von UNIX Network Programming von W. Richard Stevens - In der Tat hat der Autor von TCPv3 den Fehler gemacht, das zweite und dritte Argument in 10 Fällen des ersten Drucks gegen memset auszutauschen. Der AC-Compiler kann diesen Fehler nicht abfangen, da beide Vorkommen gleich sind. Dies war ein Fehler und konnte mit bzero vermieden werden, da das Vertauschen der beiden Argumente auf bzero vom C-Compiler immer abgefangen wird, wenn Funktionsprototypen verwendet werden. Wie paxdiablo jedoch betonte, ist bzero veraltet.
Aaron Newton
@AaronNewton, das solltest du zu Michaels Antwort hinzufügen, da es bestätigt, was er gesagt hat.
Synetech
52

Der Vorteil einer , dass ich denke , bzero()hat mehr als memset()für Null Speichereinstellung ist , dass es eine reduzierte Chance auf einen Fehler gemacht zu werden.

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

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 kleiner Reiz. (FWIW, ich wäre nicht überrascht, 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 hier zitierte Aaron Newton Folgendes aus Unix Network Programming, Band 1, 3. Auflage von Stevens et al., Abschnitt 1.2 (Hervorhebung hinzugefügt):

bzeroist keine ANSI C-Funktion. Es ist vom frühen Berkely-Netzwerkcode abgeleitet. Trotzdem verwenden wir es im gesamten Text anstelle der ANSI C- memsetFunktion, da bzeroes leichter zu merken ist (mit nur zwei Argumenten) als memset(mit drei Argumenten). Fast jeder Anbieter, der die Sockets-API unterstützt bzero, stellt auch eine Makrodefinition in unserem unp.hHeader bereit .

In dermemset Tat hat der Autor von TCPv3 [TCP / IP Illustrated, Band 3 - Stevens 1996] den Fehler gemacht, das zweite und dritte Argument in 10 Fällen im ersten Druck zu tauschen . Der AC-Compiler kann diesen Fehler nicht abfangen, da beide Argumente vom gleichen Typ sind. (Tatsächlich ist das zweite Argument ein intund das dritte Argument ist size_tnormalerweise ein unsigned int, aber die angegebenen Werte 0 und 16 sind für den anderen Argumenttyp immer noch akzeptabel.) Der Aufruf, memsetnoch zu funktionieren, weil nur a Einige der Socket-Funktionen erfordern tatsächlich, dass die letzten 8 Bytes einer Internet-Socket-Adressstruktur auf 0 gesetzt werden. Trotzdem war es ein Fehler, der durch Verwendung vermieden werden konnte bzero, weil das Vertauschen der beiden Argumente in bzeroimmer vom C-Compiler abgefangen wird, wenn Funktionsprototypen verwendet werden.

Ich glaube auch, dass die überwiegende Mehrheit der Aufrufe auf memset()Null Speicher erfolgt. Warum also nicht eine API verwenden, die auf diesen Anwendungsfall zugeschnitten ist?

Ein möglicher Nachteil bzero()besteht darin, dass Compiler möglicherweise eher optimieren, memcpy()da dies Standard ist, und daher möglicherweise so geschrieben werden, dass sie es erkennen. Beachten Sie jedoch, dass korrekter Code immer noch besser ist als falscher Code, der optimiert wurde. In den meisten Fällen wirkt sich bzero()die Verwendung nicht merklich auf die Leistung Ihres Programms aus. Dies bzero()kann eine Makro- oder Inline-Funktion sein, die erweitert wird memcpy().

Michael Burr
quelle
Ja, ich nehme an, dies könnte eine Argumentation sein, wenn Sie in einem Klassenzimmer wie diesem arbeiten, um es für die Schüler möglicherweise weniger verwirrend zu machen. Ich glaube jedoch nicht, dass dies bei meinem Professor der Fall war. Er war ein sehr großer RTFM-Lehrer. Wenn Sie eine Frage hätten, die durch das Handbuch beantwortet werden könnte, würde er im Unterricht die Manpages auf dem Projektor aufrufen und Ihnen zeigen. Es ging ihm sehr darum, allen klar zu machen, dass das Handbuch zum Lesen da ist und die meisten Ihrer Fragen beantwortet. Ich bin dafür dankbar, im Gegensatz zu einigen anderen Professoren.
PseudoPsyche
5
Ich denke, dass dies ein Argument ist, das auch außerhalb des Klassenzimmers vorgebracht werden kann - ich habe diesen Fehler im Produktionscode gesehen. Es scheint mir ein leichter Fehler zu sein. Ich würde auch vermuten, dass die überwiegende Mehrheit der memset()Anrufe einfach dazu dient, einen Speicherblock auf Null zu setzen, was meiner Meinung nach ein weiteres Argument ist bzero(). Wofür steht das 'b' überhaupt bzero()?
Michael Burr
7
+1. Das memsetverstößt gegen eine gemeinsame Parameter Bestellung von „Puffer, buffer_size“ macht es besonders fehleranfällig IMO.
Jamesdlin
In Pascal vermeiden sie das, indem sie es "fillchar" nennen und es braucht ein Zeichen. Die meisten C / C ++ - Compiler würden diesen aufgreifen. Was mich wundert, warum Compiler nicht sagen "Sie übergeben einen 32/64-Bit-Zeiger, wo ein Byte erwartet wird" und Sie fest in die Compilerfehler treten.
Móż
1
@Gewure zweites und drittes Argument sind in falscher Reihenfolge; Der zitierte Funktionsaufruf macht genau nichts
Ichthyo
4

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 (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)

Mir wurde gesagt, dass ich mir keine Sorgen machen muss , wenn ich nicht in den Tiefen von libc oder einer beliebigen Anzahl von Kernel / Syscall-Schnittstellen arbeite . Ich sollte mir nur Sorgen machen, dass der Aufruf die Anforderung erfüllt, den Puffer auf Null zu setzen. Andere haben erwähnt, welches dem anderen vorzuziehen ist, also werde ich hier aufhören.

Kaugummi
quelle
Dies liegt daran, dass einige Versionen von GCC Code ausgeben, memset(ptr, 0, n)wenn sie ihn sehen, bzero(ptr, n)und ihn nicht in Inline-Code konvertieren können.
zwol
@zwol Es ist eigentlich ein Makro.
SS Anne
1
@SSAnne gcc 9.3 auf meinem Computer führt diese Umwandlung selbst durch, ohne Hilfe von Makros in den Systemheadern. extern void bzero(void *, size_t); void clear(void *p, size_t n) { bzero(p, n); }erzeugt einen Anruf an memset. (Fügen Sie stddef.hfür size_tohne etwas anderes, das stören könnte.)
zwol
4

Sie solltenbzero es wahrscheinlich nicht verwenden , es ist eigentlich kein Standard C, es war eine POSIX-Sache.

Und beachten Sie, dass Wort „war“ - es wurde als veraltet in POSIX.1-2001 und entfernt in POSIX.1-2008 aus Rücksicht auf memset , so dass Sie besser sind aus der Standard - C - Funktion.

paxdiablo
quelle
Was meinst du mit Standard C? Sie meinen, es wird nicht in der Standard-C-Bibliothek gefunden?
Koray Tugay
@Koray, Standard C bedeutet den ISO-Standard und ist ja bzeronicht Teil davon.
Paxdiablo
Nein, ich meine, ich weiß nicht, was Sie unter einem Standard verstehen. Bedeutet ISO-Standard die Standard-C-Bibliothek? Das kommt mit der Sprache? Die minimale Bibliothek, von der wir wissen, dass sie dort sein wird?
Koray Tugay
2
@Koray, ISO ist die Standardorganisation, die für den C-Standard verantwortlich ist, der aktuelle ist C11 und die früheren C99 und C89. Sie legen die Regeln fest, denen eine Implementierung folgen muss, um als C betrachtet zu werden. Ja, wenn der Standard besagt, dass eine Implementierung ein Memset bereitstellen muss, ist sie für Sie da. Ansonsten ist es nicht C.
paxdiablo
2

Für die Memset-Funktion ist das zweite Argument ein intund das dritte Argument ist size_t:

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

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

void bzero(void *s, size_t n)

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

havish
quelle
1
Ein solcher Fehler kann auch mit memset vermieden werden, wenn Sie sich den Aufruf einfach als "Setzen Sie diesen Speicher auf diesen Wert für diese Größe" vorstellen oder wenn Sie eine IDE haben, die Ihnen den Prototyp gibt, oder wenn Sie nur wissen, was Sie sind tun :-)
paxdiablo
Stimmen Sie zu, aber diese Funktion wurde zu dem Zeitpunkt erstellt, als solche intelligenten IDEs für den Support nicht verfügbar waren.
Havish
2

Kurz gesagt: memset Dann sind mehr Montagevorgänge erforderlich bzero.

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

Tal Bar
quelle
Ja, das ist eine Sache, die ich im OP erwähnt habe. Ich habe sogar genau auf diese Seite verlinkt. Es stellt sich heraus, dass dies aufgrund einiger Compiler-Optimierungen keinen großen Unterschied zu machen scheint. Weitere Einzelheiten finden Sie in der akzeptierten Antwort von ouah.
PseudoPsyche
6
Dies zeigt nur, dass eine Müllimplementierung von memset langsam ist. Unter MacOS X und einigen anderen Systemen verwendet memset Code, der beim Booten abhängig vom verwendeten Prozessor eingerichtet wird, Vektorregister vollständig nutzt und bei großen Größen Prefetch-Anweisungen auf clevere Weise verwendet, um das letzte Bit zu erhalten der Geschwindigkeit.
Gnasher729
Weniger Anweisungen bedeuten keine schnellere Ausführung. Tatsächlich erhöhen Optimierungen häufig die Binärgröße und die Anzahl der Anweisungen aufgrund des Abrollens von Schleifen, des Inlining von Funktionen, der Ausrichtung von Schleifen ...
Wenn
2

Haben Sie es wie Sie wollen. :-)

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

Beachten Sie, dass:

  1. Das Original bzerogibt nichts zurück, memsetgibt den ungültigen Zeiger zurück (d ) zurück. Dies kann behoben werden, indem der Typecast in der Definition zu void hinzugefügt wird.
  2. #ifndef bzerohindert Sie nicht daran, die ursprüngliche Funktion zu verbergen, selbst wenn sie existiert. Es testet die Existenz eines Makros. Dies kann viel Verwirrung stiften.
  3. Es ist unmöglich, einen Funktionszeiger auf ein Makro zu erstellen. Bei Verwendung bzeroüber Funktionszeiger funktioniert dies nicht.
Bruce
quelle
1
Was ist das Problem damit, @Leeor? Allgemeine Antipathie für Makros? Oder Sie mögen es nicht, dass dieses Makro mit der Funktion verwechselt werden kann (und sie möglicherweise sogar verbirgt)?
Palec
1
@ Palec, letzteres. Das Ausblenden einer Neudefinition als Makro kann zu so viel Verwirrung führen. Ein anderer Programmierer, der diesen Code verwendet, glaubt, dass er eine Sache verwendet, und ist unwissentlich gezwungen, die andere zu verwenden. Das ist eine Zeitbombe.
Leeor
1
Nachdem ich noch einmal darüber nachgedacht habe, stimme ich zu, dass dies in der Tat eine schlechte Lösung ist. Unter anderem habe ich einen technischen Grund gefunden: Bei Verwendung bzeroüber Funktionszeiger funktioniert dies nicht.
Palec
Sie hätten Ihr Makro wirklich anders nennen sollen als bzero. Dies ist eine Gräueltat.
Dan Bechard
-2

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

Skynight
quelle