Vergleichsoperation für vorzeichenlose und vorzeichenbehaftete Ganzzahlen

Lesezeit: 7 Minuten

Benutzeravatar von Gitmo
Gitmo

Siehe dieses Code-Snippet

int main()
{ 
 unsigned int a = 1000;
 int b = -1;
 if (a>b) printf("A is BIG! %d\n", a-b);
 else printf("a is SMALL! %d\n", a-b); 
 return 0;
}   

Dies ergibt die Ausgabe: a ist KLEIN: 1001

Ich verstehe nicht, was hier passiert. Wie funktioniert hier der Operator >? Warum ist “a” kleiner als “b”? Wenn es tatsächlich kleiner ist, warum bekomme ich eine positive Zahl (1001) als Differenz?

  • Wenn Sie das Compiler-Flag -Wsign-compare verwenden, erhalten Sie eine Warnung für den Vergleich. Sie sollten immer -Wall verwenden (was -Wsign-compare enthält). Sehen hier für andere Möglichkeiten, dieses Problem zu vermeiden.

    – Alejandro

    5. Dezember 2010 um 20:58 Uhr


  • Weitere Informationen finden Sie in diesem Beitrag: stackoverflow.com/q/10474769/844882

    – Adrian Mönch

    7. Mai 2012 um 1:44 Uhr

  • @Aleph7 – technisch ist das nicht ganz richtig, -Wsign-compare ist nur inkl. mit -Wand wenn Sie kompilieren C++. Es ist nicht für C enthalten (siehe hier gcc.gnu.org/onlinedocs/gcc/Warning-Options.html) Ich habe es getestet und kann bestätigen, dass der obige Code keine Warnung mit -Wall ausgibt, aber mit -Wsign-compare (ich verwende gcc (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010)

    – bph

    10. November 2016 um 20:09 Uhr


AnT steht mit Russlands Benutzer-Avatar
AnT steht zu Russland

Binäre Operationen zwischen verschiedenen ganzzahligen Typen werden innerhalb eines “gemeinsamen” Typs ausgeführt, der durch sogenannte definiert ist übliche arithmetische Umrechnungen (siehe Sprachspezifikation, 6.3.1.8). In Ihrem Fall ist der “übliche” Typ unsigned int. Das bedeutet, dass int Operand (Ihr b) wird umgewandelt in unsigned int vor dem Vergleich sowie zum Zwecke der Subtraktion.

Wann -1 umgewandelt wird unsigned int das Ergebnis ist das maximal Mögliche unsigned int Wert (wie UINT_MAX). Unnötig zu erwähnen, dass es größer sein wird als Ihr unsigned 1000 Wert, das heißt a > b ist in der Tat falsch und a ist in der Tat klein verglichen mit (unsigned) b. Das if in Ihrem Code sollte sich auflösen else Verzweigung, was Sie in Ihrem Experiment beobachtet haben.

Für die Subtraktion gelten die gleichen Umrechnungsregeln. Dein a-b wird wirklich so interpretiert a - (unsigned) b und das Ergebnis hat Typ unsigned int. Mit einem solchen Wert kann nicht gedruckt werden %d Formatbezeichner, da %d funktioniert nur mit unterzeichnet Werte. Ihr Versuch, es mit zu drucken %d führt zu undefiniertem Verhalten, sodass der Wert, den Sie gedruckt sehen (obwohl er in der Praxis eine logische deterministische Erklärung hat), aus Sicht der C-Sprache völlig bedeutungslos ist.

Bearbeiten: Eigentlich könnte ich mich in Bezug auf den undefinierten Verhaltensteil irren. Gemäß der C-Sprachspezifikation muss der gemeinsame Teil des Bereichs des entsprechenden vorzeichenbehafteten und vorzeichenlosen ganzzahligen Typs eine identische Darstellung haben (was gemäß Fußnote 31 “Austauschbarkeit als Argumente für Funktionen” impliziert). Also das Ergebnis von a - b Ausdruck ist vorzeichenlos 1001 wie oben beschrieben, und es sei denn, ich vermisse etwas, ist es legal, diesen bestimmten unsignierten Wert mit zu drucken %d Spezifizierer, da er in den positiven Bereich von fällt int. Drucken (unsigned) INT_MAX + 1 mit %d wäre undefiniert, aber 1001u ist gut.

  • Obwohl wir genug über die Aufrufkonvention seiner Implementierung wissen oder erraten können, um zu dem Schluss zu kommen, dass das vorzeichenlose Ergebnis von ab, das tatsächlich 1001 ist, passiert ist, wurde es unbeschadet durch die Varargs geleitet und als vorzeichenbehaftet neu interpretiert, ohne den Wert zu ändern.

    – Steve Jessop

    18. Januar 2010 um 10:49 Uhr


  • Ja, unsigned int übergeben und tun va_arg(ap, int) allein ist noch nicht UB. Aber es ist in der Tat UB, die Anforderungen von printf bezüglich der Erwartung von an zu verletzen int. Für mich klingt es aber albern. Warum haben sie für printf nicht angegeben: “Der Typ des nächsten Arguments muss ein signiertes oder unsigniertes int sein und muss im Bereich von int liegen”.

    – Johannes Schaub – litb

    18. Januar 2010 um 11:13 Uhr


  • @Johannes: Eigentlich könnte es bereits angegeben sein. Siehe meine Bearbeitung.

    – AnT steht zu Russland

    18. Januar 2010 um 15:47 Uhr

  • Aber ob es eine kompatible Darstellung hat, ist nicht wichtig, denke ich. Wenn der Standard sagt, dass etwas UB ist, dann ist es UB: Es wird nicht durch irgendeine Aussage in einer Fußnote ersetzt. Ich denke, das bedeutet nur, dass Sie eine gewisse Freiheit haben, ohne harte Anforderungen an einen Teil des Standards. Wie beim Lesen von verschiedenen Gewerkschaftsmitgliedern (dies ist nicht UB vorne in C, denke ich, kann aber gut definiert werden, wenn beide Mitglieder kompatible Darstellungen haben). Aber der Aufruf einer Funktion durch einen prototyplosen Funktionszeigerausdruck mit inkompatiblen Argumenten bleibt beispielsweise UB.

    – Johannes Schaub – litb

    18. Januar 2010 um 17:49 Uhr

  • In diesem Fall, fprintf Die Beschreibung lautet: (für %d): “Das int-Argument wird konvertiert in …” und “Wenn ein Argument nicht den richtigen Typ für die entsprechende Konvertierungsspezifikation hat, ist das Verhalten undefiniert.”. Also ich glaube nicht, dass es gut definiert ist. Vielleicht kennt sich ja jemand aus dem Usenet aus?

    – Johannes Schaub – litb

    18. Januar 2010 um 17:51 Uhr

Benutzeravatar von kennytm
kennytm

Bei einer typischen Implementierung wo int ist 32-Bit, -1 bei der Konvertierung in ein unsigned int ist 4.294.967.295, was tatsächlich ≥ 1000 ist.

Auch wenn Sie die Subtraktion in an behandeln unsigned Welt, 1000 - (4,294,967,295) = -4,294,966,295 = 1,001 was du bekommst.

Deshalb gcc wird eine Warnung ausspucken, wenn Sie vergleichen unsigned mit signed. (Wenn Sie keine Warnung sehen, passieren Sie die -Wsign-compare Flagge.)

  • Ich habe wegen “4.294.967.295 (2er-Komplement)” abgelehnt. Das hat nichts mit dem Zweierkomplement zu tun. Es ergibt den gleichen Wert auf einer 1er-Komplement-Maschine. Und ergibt einen anderen Wert für eine andere Ganzzahl mit Bitbreite.

    – Johannes Schaub – litb

    18. Januar 2010 um 9:58 Uhr

  • @Schaub: Vielleicht ist mir nicht klar, aber was ich meine, ist, dass 4.294.967.295 (das 2er-Komplement von 1 ist) tatsächlich ≥1 ist. Außerdem ist die 1er-Komplement-Maschine die Darstellung von -1 4.294.967.294.

    – kennytm

    18. Januar 2010 um 10:09 Uhr


  • wie litb sagt, es hat nichts mit der darstellung zu tun. Auf einer 1er-Komplement-Maschine führt das Konvertieren von -1 in vorzeichenlose Ergebnisse in UINT_MAX dazu, dass das 1er-Komplement-Bitmuster nicht neu interpretiert wird. Eine der vielen praktischen Möglichkeiten des Zweierkomplements besteht darin, dass Umwandlungen mit Vorzeichen in C das Bitmuster nicht ändern. Das gilt insbesondere für das Zweierkomplement: Die C-Konvertierungen in vorzeichenlose Typen werden in Form von Modulo-Arithmetik definiert, nicht in Form von Bitmustern. Beim 1er-Komplement muss die Implementierung einige tatsächliche Arbeit leisten, um UINT_MAX zu erhalten.

    – Steve Jessop

    18. Januar 2010 um 10:56 Uhr


  • Die Bearbeitung ist besser, aber dafür gibt es immer noch keine Garantie UINT_MAX ist 4.294.967.295. Siehe auch stackoverflow.com/questions/1863153

    – Alok Singhal

    18. Januar 2010 um 16:15 Uhr


 #include<stdio.h>
 int main()
 {
   int a = 1000;
   signed int b = -1, c = -2;
   printf("%d",(unsigned int)b);
   printf("%d\n",(unsigned int)c);
   printf("%d\n",(unsigned int)a);

   if(1000>-1){
      printf("\ntrue");
   }
   else 
     printf("\nfalse");
     return 0;
 }

Dazu müssen Sie den Vorrang von Operatoren verstehen

  1. Relationale Operatoren funktionieren von links nach rechts … also, wenn es darum geht

    wenn (1000>-1)

dann wird zunächst -1 in eine vorzeichenlose Ganzzahl geändert, da int standardmäßig als vorzeichenlose Zahl behandelt wird und größer als die vorzeichenbehaftete Zahl ist

-1 wird zu einer vorzeichenlosen Zahl, sie wird zu einer sehr großen Zahl

Benutzeravatar von Antti Huima
Antti Huima

Sie führen einen vorzeichenlosen Vergleich durch, dh Sie vergleichen 1000 mit 2^32 – 1.

Die Ausgabe ist wegen %d in printf signiert.

NB manchmal ist das Verhalten, wenn Sie vorzeichenbehaftete und vorzeichenlose Operanden mischen, Compiler-spezifisch. Ich denke, es ist am besten, sie zu vermeiden und im Zweifelsfall zu werfen.

Finden Sie einen einfachen Weg zum Vergleichen, der möglicherweise nützlich ist, wenn Sie unsignierte Deklarationen nicht entfernen können (z. B. [NSArray count]), zwingen Sie einfach das “unsigned int” zu einem “int”.

Bitte korrigieren Sie mich, wenn ich falsch liege.

if (((int)a)>b) {
    ....
}

Benutzeravatar von DigitalRoss
DigitalRoss

Die Hardware ist so ausgelegt, dass sie signiert mit signiert und unsigniert mit unsigniert vergleicht.

Wenn Sie das arithmetische Ergebnis wünschen, konvertieren Sie den vorzeichenlosen Wert zuerst in einen größeren vorzeichenbehafteten Typ. Andernfalls geht der Compiler davon aus, dass der Vergleich wirklich zwischen vorzeichenlosen Werten erfolgt.

Und -1 wird als 1111..1111 dargestellt, also eine sehr große Menge … Die größte … Wenn sie als vorzeichenlos interpretiert wird.

Benutzeravatar von Naveen Kumar
Naveen Kumar

beim Vergleich von a>b, wobei a ein vorzeichenloser Int-Typ und b ein Int-Typ ist, b wird in unsigned int typgecastet also wird der vorzeichenbehaftete int-Wert -1 in den MAX-Wert von unsigned** (Bereich: 0 bis (2^32)-1)** umgewandelt. Somit wird a>b, dh (1000>4294967296) falsch. Daher sonst Schleife printf(“a ist KLEIN! %d\n”, ab); hingerichtet.

  • Dies ist ein Duplikat der am häufigsten bewerteten Antwort mit geringerem Aufwand

    – Emsimpson92

    9. Juli 2018 um 23:00 Uhr

1411820cookie-checkVergleichsoperation für vorzeichenlose und vorzeichenbehaftete Ganzzahlen

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy