Ich habe unten ein einfaches Programm:
#include <stdio.h>
#define INT32_MIN (-0x80000000)
int main(void)
{
long long bal = 0;
if(bal < INT32_MIN )
{
printf("Failed!!!");
}
else
{
printf("Success!!!");
}
return 0;
}
Die Bedingung if(bal < INT32_MIN )
ist immer wahr. Wie ist es möglich?
Es funktioniert gut, wenn ich das Makro in ändere:
#define INT32_MIN (-2147483648L)
Kann jemand auf das Problem hinweisen?
c
signed
numeric-limits
numeric-conversion
Jayesh Bhoi
quelle
quelle
CHAR_BIT * sizeof(int)
?-0x80000000
, aber falsch für-0x80000000L
,-2147483648
und-2147483648L
(gcc 4.1.2), so ist die Frage: warum ist der int wörtlichen-0x80000000
unterscheidet sich von der int wörtliche-2147483648
?<limits.h>
definiertINT_MIN
als(-2147483647 - 1)
, wissen Sie jetzt warum.Antworten:
Das ist ziemlich subtil.
Jedes ganzzahlige Literal in Ihrem Programm hat einen Typ. Welchen Typ es hat, wird durch eine Tabelle in 6.4.4.1 geregelt:
Wenn eine Literalzahl nicht in den Standardtyp passt
int
, wird der nächstgrößere Typ wie in der obigen Tabelle angegeben versucht. Für reguläre Dezimalzahl-Literale lautet es also wie folgt:int
long
long long
.Hex-Literale verhalten sich jedoch anders! Wenn das Literal nicht in einen signierten Typ wie passt
int
, wird es zuerst versucht,unsigned int
bevor größere Typen ausprobiert werden. Siehe den Unterschied in der obigen Tabelle.Auf einem 32-Bit-System ist Ihr Literal
0x80000000
also vom Typunsigned int
.Dies bedeutet, dass Sie den unären
-
Operator auf das Literal anwenden können, ohne ein implementierungsdefiniertes Verhalten aufzurufen, wie Sie es sonst tun würden, wenn eine vorzeichenbehaftete Ganzzahl überläuft. Stattdessen erhalten Sie den Wert0x80000000
, einen positiven Wert.bal < INT32_MIN
ruft die üblichen arithmetischen Konvertierungen auf und das Ergebnis des Ausdrucks0x80000000
wird vonunsigned int
bis heraufgestuftlong long
. Der Wert0x80000000
bleibt erhalten und 0 ist kleiner als 0x80000000, daher das Ergebnis.Wenn Sie das Literal durch das ersetzen, verwenden
2147483648L
Sie die Dezimalschreibweise. Daher wählt der Compiler nicht ausunsigned int
, sondern versucht, es in a einzufügenlong
. Auch das Suffix L sagt, dass Sie einlong
wenn möglich wollen . Das L-Suffix hat tatsächlich ähnliche Regeln, wenn Sie die in 6.4.4.1 erwähnte Tabelle weiter lesen: Wenn die Zahl nicht in die angeforderte passtlong
, was im 32-Bit-Fall nicht der Fall ist, gibt Ihnen der Compiler einen Ort, anlong long
dem sie steht wird gut passen.quelle
long
System2147483648L
wird nicht in ein passenlong
, so wird eslong long
, dann wird das-
angewendet - oder so dachte ich.0x7FFFFFFF
. Probieren Sie es aus:#include <limits.h> printf("%X\n", INT_MAX);
0x7FFFFFFF
im Quellcode geschriebene Literal ist immer eine positive Zahl, aber Ihreint
Variable kann natürlich rohe Binärzahlen bis zum Wert 0xFFFFFFFF enthalten.ìnt n = 0x80000000
erzwingt eine Konvertierung vom vorzeichenlosen Literal in einen vorzeichenbehafteten Typ. Was passieren wird, liegt bei Ihrem Compiler - es ist ein implementierungsdefiniertes Verhalten. In diesem Fall wurde das gesamte Literal inint
das Vorzeichenbit eingefügt. Auf anderen Systemen ist es möglicherweise nicht möglich, den Typ darzustellen, und Sie rufen undefiniertes Verhalten auf - das Programm kann abstürzen. Wenn Sie dies tunint n=2147483648;
, erhalten Sie das gleiche Verhalten, das überhaupt nicht mit der Hex-Notation zusammenhängt.-
auf vorzeichenlose Ganzzahlen angewendet wird, könnte etwas erweitert werden. Ich hatte immer angenommen (obwohl ich mich glücklicherweise nie auf die Annahme verlassen hatte), dass vorzeichenlose Werte zu vorzeichenbehafteten Werten "heraufgestuft" würden oder dass das Ergebnis möglicherweise undefiniert wäre. (Ehrlich gesagt sollte es ein Kompilierungsfehler sein; was bedeutet das- 3u
überhaupt?)0x80000000
ist einunsigned
Literal mit dem Wert 2147483648.Die Anwendung des unären Minus auf diese noch gibt Ihnen einen unsigned - Typ mit einem Wert ungleich Null. (Für einen Wert ungleich Null ist der Wert
x
, den Sie am Ende erhaltenUINT_MAX - x + 1
.)quelle
Dieses ganzzahlige Literal
0x80000000
hat den Typunsigned int
.Nach dem C-Standard (6.4.4.1 Integer-Konstanten)
Und diese ganzzahlige Konstante kann durch den Typ von dargestellt werden
unsigned int
.Also dieser Ausdruck
-0x80000000
hat den gleichenunsigned int
Typ. Darüber hinaus hat es den gleichen Wert0x80000000
in der Komplementdarstellung der beiden, der die folgende Methode berechnetDies hat einen Nebeneffekt, wenn zum Beispiel geschrieben werden soll
Das Ergebnis wird wieder sein
INT_MIN
.Also in diesem Zustand
Es wird
0
mit einem vorzeichenlosen Wert verglichen0x80000000
, der gemäß den Regeln der üblichen arithmetischen Konvertierungen in den Typ long long int konvertiert wurde.Es ist offensichtlich, dass 0 kleiner als ist
0x80000000
.quelle
Die numerische Konstante
0x80000000
ist vom Typunsigned int
. Wenn wir-0x80000000
2s Komplimente machen und Mathe machen, bekommen wir folgendes:Also
-0x80000000 == 0x80000000
. Und Vergleichen(0 < 0x80000000)
(da0x80000000
nicht signiert ist) ist wahr.quelle
int
s voraus . Obwohl dies eine sehr häufige Wahl ist, kann jede Implementierungint
entweder enger oder breiter sein. Es ist jedoch eine korrekte Analyse für diesen Fall.-0x80000000
ist eine vorzeichenlose Arithmetik.~0x800000000
ist anderer Code.-0x80000000
! Tatsächlich ist die Ergänzung von 2 für diese Frage völlig irrelevant.Ein Punkt der Verwirrung tritt auf, wenn man denkt, dass dies
-
Teil der numerischen Konstante ist.Im folgenden Code
0x80000000
ist die numerische Konstante. Sein Typ ist nur darauf bestimmt. Das-
wird danach angewendet und ändert den Typ nicht .Rohe schmucklose numerische Konstanten sind positiv.
Wenn es dezimal ist, dann zugewiesen ist der Typ erste Art , die es halten wird:
int
,long
,long long
.Wenn die Konstante Oktal oder hexadezimal ist, wird es die erste Art , die es gilt:
int
,unsigned
,long
,unsigned long
,long long
,unsigned long long
.0x80000000
, auf dem OP-System erhält den Typ vonunsigned
oderunsigned long
. In beiden Fällen handelt es sich um einen vorzeichenlosen Typ.-0x80000000
ist auch ein Wert ungleich Null und ein Typ ohne Vorzeichen, der größer als 0 ist. Wenn Code dies mit a vergleichtlong long
, werden die Werte auf den beiden Seiten des Vergleichs nicht geändert,0 < INT32_MIN
was wahr ist.Eine alternative Definition vermeidet dieses merkwürdige Verhalten
Lass uns eine Weile im Fantasieland spazieren gehen, wo
int
undunsigned
sind 48-Bit.Dann
0x80000000
passt hineinint
und so ist der Typint
.-0x80000000
ist dann eine negative Zahl und das Ergebnis des Ausdrucks ist unterschiedlich.[Zurück zum richtigen Wort]
Da es
0x80000000
in einen vorzeichenlosen Typ vor einem vorzeichenbehafteten Typ passt, da dieser nur größer alssome_signed_MAX
nochsome_unsigned_MAX
darin ist, handelt es sich um einen vorzeichenlosen Typ.quelle
C hat die Regel, dass das ganzzahlige Literal sein kann
signed
oderunsigned
davon abhängt, ob es insigned
oder passtunsigned
(ganzzahlige Heraufstufung). Auf einer32
Bit-Maschine wird das Literal0x80000000
seinunsigned
. Das Komplement von 2-0x80000000
befindet sich0x80000000
auf einem 32-Bit-Computer. Daher erfolgt der Vergleichbal < INT32_MIN
zwischensigned
undunsigned
und vor dem Vergleich gemäß der C-Regelunsigned int
wird in konvertiertlong long
.C11: 6.3.1.8/1:
Deshalb
bal < INT32_MIN
ist immertrue
.quelle