Was Ihnen begegnet ist, ist ein besonderes Verhalten, das in C auftritt, wenn Ausdrücke verarbeitet werden, die sowohl vorzeichenbehaftete als auch vorzeichenlose Mengen enthalten.
Wenn eine Operation ausgeführt wird, bei der ein Operand signiert und der andere nicht signiert ist, konvertiert C das signierte Argument implizit in ein nicht signiertes und führt die Operationen unter der Annahme aus, dass die Zahlen nicht negativ sind. Diese Konvention führt häufig zu nicht intuitivem Verhalten für Vergleichsoperatoren wie <
und >
.
Beachten Sie in Bezug auf Ihre Hilfsfunktion, dass die Differenz und der Vergleich beide mit vorzeichenloser Arithmetik berechnet werden , da der strlen
Rückgabetyp size_t
(eine vorzeichenlose Menge) zurückgegeben wird. Wenn s1
kürzer als ist s2
, sollte die Differenz strlen(s1) - strlen(s2)
negativ sein, wird jedoch zu einer großen vorzeichenlosen Zahl, die größer als ist 0
. So,
return strlen(s1) - strlen(s2) > 0;
gibt zurück, 1
auch wenn s1
kürzer als ist s2
. Verwenden Sie stattdessen diesen Code, um Ihre Funktion zu reparieren:
return strlen(s1) > strlen(s2);
Willkommen in der wundervollen Welt von C! :) :)
Zusätzliche Beispiele
Da diese Frage in letzter Zeit viel Aufmerksamkeit erhalten hat, möchte ich einige (einfache) Beispiele nennen, um sicherzustellen, dass ich die Idee vermitteln kann. Ich gehe davon aus, dass wir mit einer 32-Bit-Maschine arbeiten, die die Zweierkomplementdarstellung verwendet.
Das wichtige Konzept, das beim Arbeiten mit vorzeichenlosen / vorzeichenbehafteten Variablen in C zu verstehen ist, besteht darin, dass vorzeichenbehaftete Werte implizit in vorzeichenlose Werte umgewandelt werden , wenn in einem einzelnen Ausdruck eine Mischung aus vorzeichenlosen und vorzeichenbehafteten Größen vorhanden ist .
Beispiel 1:
Betrachten Sie den folgenden Ausdruck:
-1 < 0U
Da der zweite Operand ohne Vorzeichen ist, wird der erste implizit in ohne Vorzeichen umgewandelt, und daher entspricht der Ausdruck dem Vergleich.
4294967295U < 0U
was natürlich falsch ist. Dies ist wahrscheinlich nicht das Verhalten, das Sie erwartet haben.
Beispiel 2:
Betrachten Sie den folgenden Code, der versucht, die Elemente eines Arrays zu summieren a
, wobei die Anzahl der Elemente durch Parameter angegeben wird length
:
int sum_array_elements(int a[], unsigned length) {
int i;
int result = 0;
for (i = 0; i <= length-1; i++)
result += a[i];
return result;
}
Diese Funktion soll zeigen, wie leicht Fehler durch implizites Casting von signiert zu nicht signiert auftreten können. Es scheint ganz natürlich, Parameter length
als vorzeichenlos zu übergeben. Wer würde schon mal eine negative Länge verwenden wollen? Das Stoppkriterium i <= length-1
scheint auch ziemlich intuitiv zu sein. Wenn jedoch mit einem Argument length
gleich ausgeführt wird, 0
führt die Kombination dieser beiden zu einem unerwarteten Ergebnis.
Da der Parameter length
vorzeichenlos ist, wird die Berechnung 0-1
mit vorzeichenloser Arithmetik durchgeführt, was einer modularen Addition entspricht. Das Ergebnis ist dann UMax . Der <=
Vergleich wird auch unter Verwendung eines vorzeichenlosen Vergleichs durchgeführt, und da jede Zahl kleiner oder gleich UMax ist , gilt der Vergleich immer. Daher versucht der Code, auf ungültige Elemente des Arrays zuzugreifen a
.
Der Code kann entweder durch Deklarieren length
als int
oder durch Ändern des Tests der for
Schleife festgelegt werden i < length
.
Fazit: Wann sollten Sie Unsigned verwenden?
Ich möchte hier nichts zu kontroverses sagen, aber hier sind einige der Regeln, die ich oft einhalte, wenn ich Programme in C schreibe.
NICHT verwenden, nur weil eine Zahl nicht negativ ist. Es ist leicht, Fehler zu machen, und diese Fehler sind manchmal unglaublich subtil (wie in Beispiel 2 dargestellt).
Verwenden Sie diese Option , wenn Sie modulare Arithmetik ausführen.
Verwenden Sie diese Option , wenn Sie Bits zur Darstellung von Mengen verwenden. Dies ist häufig praktisch, da Sie damit logische Rechtsverschiebungen ohne Vorzeichenerweiterung durchführen können.
Natürlich kann es Situationen geben, in denen Sie sich gegen diese "Regeln" entscheiden. In den meisten Fällen erleichtert das Befolgen dieser Vorschläge die Arbeit mit Ihrem Code und ist weniger fehleranfällig.
return strlen(s1) > strlen(s2);
.