Mam poniżej prosty program:
#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;
}
Warunek if(bal < INT32_MIN )
jest zawsze spełniony. Jak to jest możliwe?
Działa dobrze, jeśli zmienię makro na:
#define INT32_MIN (-2147483648L)
Czy ktoś może wskazać problem?
c
signed
numeric-limits
numeric-conversion
Jayesh Bhoi
źródło
źródło
CHAR_BIT * sizeof(int)
?-0x80000000
, ale za fałszywe-0x80000000L
,-2147483648
a-2147483648L
(gcc 4.1.2), więc pytanie brzmi: dlaczego jest int dosłowne-0x80000000
różni się od int dosłownym-2147483648
?<limits.h>
definiujeINT_MIN
jako(-2147483647 - 1)
, teraz wiesz, dlaczego.Odpowiedzi:
To jest dość subtelne.
Każdy literał całkowity w twoim programie ma swój typ. Jaki typ ma regulowany jest tabelą w 6.4.4.1:
Jeśli dosłowna liczba nie zmieści się w
int
typie domyślnym , spróbuje użyć następnego większego typu, jak wskazano w powyższej tabeli. Tak więc w przypadku zwykłych liczb całkowitych dziesiętnych wygląda to tak:int
long
long long
.Hex literały zachowują się jednak inaczej! Jeśli literał nie zmieści się w podpisanym typie
int
, najpierw spróbuje,unsigned int
zanim przejdzie do wypróbowania większych typów. Zobacz różnicę w powyższej tabeli.Tak więc w systemie 32-bitowym twój literał
0x80000000
jest typowyunsigned int
.Oznacza to, że możesz zastosować jednoargumentowy
-
operator do literału bez wywoływania zachowania zdefiniowanego w implementacji, tak jak w przypadku przepełnienia liczby całkowitej ze znakiem. Zamiast tego otrzymasz wartość0x80000000
, wartość dodatnią.bal < INT32_MIN
wywołuje zwykłe konwersje arytmetyczne, a wynik wyrażenia0x80000000
jest promowany odunsigned int
dolong long
. Wartość0x80000000
zostaje zachowana, a 0 jest mniejsze niż 0x80000000, stąd wynik.Kiedy zamieniasz literał na
2147483648L
, użyj notacji dziesiętnej i dlatego kompilator nie wybieraunsigned int
, ale raczej próbuje dopasować go dolong
. Również przyrostek L mówi, że chcesz,long
jeśli to możliwe . Sufiks L faktycznie ma podobne zasady, jeśli nadal czytasz wspomnianą tabelę w 6.4.4.1: jeśli liczba nie mieści się w żądanymlong
, co nie jest w przypadku 32-bitowym, kompilator da ci miejsce, wlong long
którym będzie dobrze pasować.źródło
long
systemie2147483648L
, nie zmieści się w sposóblong
, dzięki czemu staje sięlong long
, następnie-
stosowana jest - przynajmniej tak mi się wydawało.0x7FFFFFFF
. Spróbuj sam:#include <limits.h> printf("%X\n", INT_MAX);
0x7FFFFFFF
zapisana w kodzie źródłowym jest zawsze liczbą dodatnią, ale twojaint
zmienna może oczywiście zawierać nieprzetworzone liczby binarne do wartości 0xFFFFFFFF.ìnt n = 0x80000000
wymusza konwersję z niepodpisanego literału na typ podpisany. To, co się stanie, zależy od twojego kompilatora - jest to zachowanie zdefiniowane w implementacji. W tym przypadku postanowił pokazać cały literał wint
, zastępując bit znaku. W innych systemach może nie być możliwe przedstawienie tego typu i wywołanie niezdefiniowanego zachowania - program może ulec awarii. Otrzymasz takie samo zachowanie, jeśli to zrobisz, nieint n=2147483648;
będzie to wcale związane z notacją heksadecymalną.-
jest stosowane do liczb całkowitych bez znaku, można nieco rozszerzyć. Zawsze zakładałem (choć na szczęście nigdy nie opierałem się na założeniu), że niepodpisane wartości będą „promowane” do wartości podpisanych, a być może wynik będzie niezdefiniowany. (Szczerze mówiąc, powinien to być błąd kompilacji; co to w- 3u
ogóle oznacza?)0x80000000
jestunsigned
literałem o wartości 2147483648.Zastosowanie jednokierunkowego minus na tym nadal daje typ bez znaku z wartością niezerową. (W rzeczywistości, dla wartości niezerowej, wartość
x
, którą się kończy toUINT_MAX - x + 1
.)źródło
Ten literał całkowity
0x80000000
ma typunsigned int
.Według normy C (6.4.4.1 Stałe całkowite)
Ta stała całkowita może być reprezentowana przez typ
unsigned int
.Więc to wyrażenie
-0x80000000
ma ten samunsigned int
typ. Ponadto ma tę samą wartość0x80000000
w reprezentacji uzupełnienia dwóch, która oblicza w następujący sposóbMa to efekt uboczny, jeśli na przykład piszesz
Wynik będzie znowu
INT_MIN
.Tak więc w tym stanie
porównuje się go
0
z wartością bez znaku0x80000000
konwertowaną na typ long long int zgodnie z regułami zwykłych konwersji arytmetycznych.Oczywiste jest, że 0 jest mniejsze niż
0x80000000
.źródło
Stała liczbowa
0x80000000
jest typuunsigned int
. Jeśli weźmiemy-0x80000000
i wykonamy na nim 2s komplement matematyki, otrzymamy to:Tak
-0x80000000 == 0x80000000
. A porównywanie(0 < 0x80000000)
(ponieważ0x80000000
jest niepodpisane) jest prawdziwe.źródło
int
s. Chociaż jest to bardzo powszechny wybór, w każdej implementacjiint
może być węższy lub szerszy. Jest to jednak poprawna analiza dla tego przypadku.-0x80000000
jest arytmetyką bez znaku.~0x800000000
to inny kod.-0x80000000
! W rzeczywistości uzupełnienie 2 nie ma znaczenia dla tego pytania.Punktem zamieszania jest myślenie, że
-
jest częścią stałej liczbowej.W poniższym kodzie
0x80000000
jest stała liczbowa. Jego typ określa się tylko na tym.-
Nakłada potem i nie zmienia typ .Surowe, nie ozdobione stałe liczbowe są dodatnie.
Jeśli jest dziesiętny, a następnie typ przypisany jest pierwszy typ, który będzie posiadać:
int
,long
,long long
.Jeśli stała jest ósemkowym lub szesnastkowym, robi pierwszy typ, który trzyma go:
int
,unsigned
,long
,unsigned long
,long long
,unsigned long long
.0x80000000
, w systemie OP pobiera typunsigned
lubunsigned long
. Tak czy inaczej, jest to jakiś niepodpisany typ.-0x80000000
jest również pewną niezerową wartością, a ponieważ jest pewnym typem bez znaku, jest większa niż 0. Gdy kod porównuje to do along long
, wartości nie są zmieniane po 2 stronach porównania, więc0 < INT32_MIN
jest to prawda.Alternatywna definicja pozwala uniknąć tego dziwnego zachowania
Chodźmy w krainie fantasy przez chwilę, gdzie
int
iunsigned
są 48-bitowe.Następnie
0x80000000
pasujeint
i taki jest typint
.-0x80000000
jest wtedy liczbą ujemną, a wynik wydruku jest inny.[Powrót do prawdziwego słowa]
Ponieważ
0x80000000
pasuje do jakiegoś typu bez znaku przed typem ze znakiem, ponieważ jest tylko większy niżsome_signed_MAX
wewnątrzsome_unsigned_MAX
, jest to pewien typ bez znaku.źródło
C ma zasadę, że literał całkowity może być
signed
lubunsigned
zależy od tego, czy pasuje dosigned
czyunsigned
(promocja liczb całkowitych). Na32
maszynie -bitowej literał0x80000000
będzieunsigned
. Uzupełnienie 2-0x80000000
znajduje się0x80000000
na komputerze 32-bitowym. Dlatego porównaniebal < INT32_MIN
jest pomiędzysigned
iunsigned
przed porównaniem zgodnie z regułą Cunsigned int
zostanie przekonwertowane nalong long
.C11: 6.3.1.8/1:
Dlatego
bal < INT32_MIN
zawsze jesttrue
.źródło