Kann ich memcpy () und memmove () aufrufen, wobei "Anzahl der Bytes" auf Null gesetzt ist?

102

Muss ich Fälle behandeln, in denen ich tatsächlich nichts zum Verschieben / Kopieren mit memmove()/ memcpy()als Randfälle habe?

int numberOfBytes = ...
if( numberOfBytes != 0 ) {
    memmove( dest, source, numberOfBytes );
}

oder sollte ich einfach die Funktion aufrufen ohne zu überprüfen

int numberOfBytes = ...
memmove( dest, source, numberOfBytes );

Ist das Einchecken in das frühere Snippet notwendig?

scharfer Zahn
quelle
6
Die Frage erinnert mich ein wenig daran, bei Funktionen wie free nach Nullzeigern zu suchen. Nicht notwendig, aber ich würde dort einen Kommentar einfügen, um Ihnen zu zeigen, dass Sie darüber nachgedacht haben.
Kröte
12
@Toad: Welchen Zweck hat das, außer den Code zu überladen? Wenn ich den Code von jemandem lese, muss ich nicht wissen, dass der ursprüngliche Programmierer "über diese Operation nachgedacht hat, die eigentlich nicht notwendig ist, aber weil sie unnötig ist, habe ich sie nicht gemacht". Wenn ich sehe, dass ein Zeiger freigegeben wird, weiß ich, dass er null sein darf, daher muss ich die Gedanken des ursprünglichen Programmierers zum Thema "sollte ich nach null suchen" nicht kennen. Und das gleiche gilt für das Kopieren von 0 Bytes mitmemcpy
Jalf
8
@jalf: Die Tatsache, dass es sich um eine Frage zum Stackoverflow handelt, lässt die Leute daran zweifeln. Das Hinzufügen eines Kommentars hilft Ihnen möglicherweise nicht, aber möglicherweise jemandem mit weniger Wissen
Toad
2
@Toad Ja, Kommentare, die explizit darauf hinweisen, warum eine Prüfung, die wirklich notwendig erscheint, nicht wirklich wertvoll sein kann. Die andere Seite der Medaille ist, dass dieses spezielle Beispiel ein häufiger Fall ist, der eine Standardbibliotheksfunktion beinhaltet, auf die jeder Programmierer die Antwort nur einmal lernen muss. dann können sie in jedem Programm, das sie lesen, erkennen, dass diese Überprüfungen nicht erforderlich sind. Aus diesem Grund würde ich die Kommentare weglassen. Eine Codebasis mit mehreren Aufrufen wie diesem muss entweder die Kommentare kopieren und in jeden einfügen oder sie willkürlich nur für einige Aufrufe verwenden, die beide hässlich sind.
Mark Amery

Antworten:

145

Aus dem C99-Standard (7.21.1 / 2):

Wenn ein als deklariertes Argument size_t ndie Länge des Arrays für eine Funktion angibt, nkann es bei einem Aufruf dieser Funktion den Wert Null haben. Sofern in der Beschreibung einer bestimmten Funktion in diesem Unterabschnitt nicht ausdrücklich anders angegeben, müssen Zeigerargumente für einen solchen Aufruf weiterhin gültige Werte haben, wie in 7.1.4 beschrieben. Bei einem solchen Aufruf findet eine Funktion, die ein Zeichen findet, kein Vorkommen, eine Funktion, die zwei Zeichenfolgen vergleicht, gibt Null zurück, und eine Funktion, die Zeichen kopiert, kopiert Null Zeichen.

Die Antwort lautet also nein; Die Prüfung ist nicht erforderlich (oder ja; Sie können Null übergeben).

Mike Seymour
quelle
1
Würde ein Zeiger für die Zwecke einer solchen Funktion als "gültig" angesehen, wenn er auf die Position nach dem letzten Element eines Arrays verweist? Ein solcher Zeiger konnte nicht legitimerweise zurückgestellt werden, aber man konnte sicher einige andere zeigerähnliche Dinge tun, wie einen davon zu subtrahieren.
Supercat
1
@supercat: Ja, ein Zeiger, der einen nach dem Ende eines Arrays zeigt, gilt für die Zeigerarithmetik mit anderen Zeigern innerhalb (oder nach dem Ende) dieses Arrays, ist jedoch nicht dereferenzierbar.
Mike Seymour
@ MikeSeymour: Sollte das Zitat nicht eine entgegengesetzte Antwort implizieren: Die Prüfung ist notwendig und Sie können mit Nullzeigern keine Null übergeben?
Neverhoodboy
7
@neverhoodboy: Nein, das Zitat sagt eindeutig " nkann den Wert Null haben". Sie haben Recht, dass Sie keine Nullzeiger übergeben können, aber darum geht es in der Frage nicht.
Mike Seymour
1
@ MikeSeymour: Meine Schuld. Wirklich leid. Die Frage betrifft die Größe, nicht den Zeiger.
Neverhoodboy
5

Wie von @You gesagt, gibt der Standard an, dass memcpy und memmove diesen Fall problemlos behandeln sollen. da sie normalerweise irgendwie wie implementiert sind

void *memcpy(void *_dst, const void *_src, size_t len)
{
    unsigned char *dst = _dst;
    const unsigned char *src = _src;
    while(len-- > 0)
        *dst++ = *src++;
    return _dst;
}

Sie sollten nicht einmal eine andere Leistungsminderung als den Funktionsaufruf haben. Wenn der Compiler Intrinsics / Inlining für solche Funktionen unterstützt, kann die zusätzliche Überprüfung den Code sogar ein wenig langsamer machen, da die Überprüfung bereits zu diesem Zeitpunkt durchgeführt wird.

Matteo Italia
quelle
1
Ich würde denken, dass diese Funktion wahrscheinlich in Assembly gemacht wird, wo Sie Speicherübertragungen viel besser optimieren können als in c
Toad
"irgendwie wie" :) Eigentlich sind fast alle Implementierungen, die ich gesehen habe, in Assembly und versuchen, die meisten Bits mit der nativen Wortgröße zu kopieren (z. B. uint32_t auf x86), aber das ändert nichts an der Substanz der Antwort : Es ist eine while- Schleife, die vor dem Start keine großen Berechnungen benötigt, daher ist die Überprüfung bereits abgeschlossen.
Matteo Italia
9
-1 ist die typische Implementierung irrelevant, ob es gültig ist, diese Funktionen (die möglicherweise nicht einmal als C-Funktionen implementiert sind) mit einem Null-Argument aufzurufen.
R .. GitHub STOP HELPING ICE
4
Die Tatsache, dass es gültig ist, wurde bereits von den anderen Antworten abgedeckt, wie ich ganz am Anfang meiner Antwort sagte: "Wie von @You gesagt, legt der Standard fest, dass memcpy und memmove diesen Fall problemlos behandeln sollen." Ich habe gerade meine Meinung dazu hinzugefügt, dass Sie aus Leistungsgründen nicht einmal Angst haben sollten, memcpy mit len ​​= 0 anzurufen, da es sich in diesem Fall um einen Anruf mit fast null Kosten handelt.
Matteo Italia