David Heffernans Kommentar ist korrekt. Dieser Code ist erschreckend.
Der Punkt des Codes, über den Sie nachfragen, ist das Überspringen der Dereferenzierung q
, wenn es null ist. Daher glaubt der Autor des Codes, dass q
null sein könnte. Unter welchen Umständen kann q
null sein? Das offensichtlichste ist: wenn null ist.
Also mal sehen, was der Code macht, wenn null ist.
Also das erste, was wir tun, ist q dekrementieren, was null ist.Wahrscheinlich wird dies umwickeln und wir werden als Ergebnis erhalten q enthält die größten möglichen Zeiger.
Da null kleiner als alles außer null ist und q nicht mehr null ist, ist dies der Fall. Wir betreten die Schleife ...
++p, --q)
*p = *p^*q,
Und sofort Dereferenzierung Null.
*q = *p^*q,
*p = *p^*q;
}
übrigens bei Coverity verweisen wir auf diese als „zukunfts Null-Fehler“ - das ist das Muster, in dem ein Codepfad gibt an, dass ein Wert möglicherweise null erwartet wird, und dann später auf dem gleichen Der Codepfad geht davon aus, dass es nicht null ist. Es ist sehr häufig.
OK, also ist dieser Code vollständig gebrochen, wenn er als Argument null ist. Gibt es andere Möglichkeiten, dass es kaputt ist? Was passiert, wenn wir eine leere Zeichenfolge eingeben?
void strrev(char *p) // Assumption: *p is 0
{
char *q = p; // *q is 0
while(q && *q) ++q; // The second condition is not met so the body is skipped.
for(--q; // q now points *before valid memory*.
p < q // And we compare an invalid pointer to a valid one.
Haben wir eine Garantie in C haben, die besagt, dass, wenn Sie aus einem Zeiger auf gültige Speicher subtrahieren, und dann diesen Zeiger auf einen anderen Zeiger vergleichen zu können, dass der Vergleich sinnvoll ist? Weil mir das als unglaublich gefährlich erscheint. Ich kenne den C-Standard nicht gut genug, um zu sagen, ob dies ein undefiniertes Verhalten ist oder nicht.
Darüber hinaus verwendet dieser Code den schrecklichen "zwei Zeichen mit xor" Trick tauschen. Warum in aller Welt würde das jemand tun? Es erzeugt größeren, langsameren Maschinencode und ist schwerer zu lesen, zu verstehen und zu warten. Wenn Sie zwei Dinge austauschen möchten, tauschen Sie sie.
Es verwendet auch den Komma-Operator, um mehrere Anweisungen in einer einzigen Anweisung zu setzen, um den Horror von Klammern um den Körper der for
zu vermeiden. Was ist der Zweck dieser Kuriosität? Der Zweck des Codes ist nicht zu zeigen, wie viele Betreiber Sie kennen, ist es in erster Linie zu mit dem Leser des Codes kommunizieren.
Die Funktion ändert auch ihren formalen Parameter, was das Debuggen erschwert.
Es schützt die Funktion vor dem Zurückstellen eines NULL-Pointerarguments. –
Beachten Sie, dass dieses Programm nicht der Spezifikation entspricht, da der "Namensraum" 'str *' für Funktionen reserviert ist. – unwind
Dieser Code ist einfach entsetzlich. Es wäre viel besser, wenn du es wegwirfst. –