Neden 0 <-0x80000000?


253

Aşağıda basit bir program var:

#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;
}

Durum if(bal < INT32_MIN )her zaman doğrudur. Bu nasıl mümkün olaiblir?

Makroyu şu şekilde değiştirirsem iyi çalışır:

#define INT32_MIN        (-2147483648L)

Sorunu bilen var mı?


3
Ne kadar CHAR_BIT * sizeof(int)?
5gon12eder

1
Bal yazdırmayı denedin mi?
Ryan Fitzpatrick

10
IMHO daha ilginç olan bu doğrudur olmasıdır yalnızca için -0x80000000ancak sahte -0x80000000L, -2147483648ve -2147483648Lneden int literal bağlıdır: (gcc 4.1.2), soru yüzden -0x80000000int sabitin farklı -2147483648?
Andreas Fester

2
@Bathsheba Ben sadece online derleyici üzerinde programı çalışıyorum tutorialspoint.com/codingground.htm
Jayesh Bhoi

2
Hiç fark (bazı enkarnasyonlar arasında) ettiyseniz <limits.h>tanımlar INT_MINolarak (-2147483647 - 1), şimdi neden biliyor.
zwol

Yanıtlar:


363

Bu oldukça ince.

Programınızdaki her tam sayı değişmezinin bir türü vardır. Hangi tipte olduğu 6.4.4.1'deki bir tablo tarafından düzenlenir:

Suffix      Decimal Constant    Octal or Hexadecimal Constant

none        int                 int
            long int            unsigned int
            long long int       long int
                                unsigned long int
                                long long int
                                unsigned long long int

Değişmez bir sayı varsayılan inttürün içine sığmazsa , yukarıdaki tabloda gösterildiği gibi bir sonraki büyük türü dener. Yani düzenli ondalık tam sayı değişmez değerleri için şu şekilde olur:

  • Deneyin int
  • Uymuyorsa, deneyin long
  • Uygun değilse, deneyin long long.

Hex değişmezler farklı davranıyor! Değişmez gibi işaretli bir türe sığamazsa int, unsigned intdaha büyük türleri denemeden önce dener . Yukarıdaki tablodaki farka bakınız.

32 bit sistemde Yani, edebi 0x80000000tiptedir unsigned int.

Başka bir -deyişle, işaretli bir tamsayıyı taşırken yaptığınız gibi, uygulama tanımlı davranışı çağırmadan tekli işleci hazır bilgi üzerine uygulayabilirsiniz. Bunun yerine, değeri 0x80000000, pozitif bir değeri alırsınız .

bal < INT32_MINHer zamanki aritmetik dönüşüm ve ifade sonucu çağırır 0x80000000yükseltilir unsigned intiçin long long. Değer 0x80000000korunur ve 0, 0x80000000'den küçüktür, dolayısıyla sonuç.

Değişmez değeri sizinle değiştirdiğinizde, 2147483648Londalık gösterimi kullanın ve bu nedenle derleyici seçim yapmaz unsigned int, aksine bunu a içine sığdırmaya çalışır long. Ayrıca L soneki long mümkünse bir istediğinizi söylüyor . 6.4.4.1'de belirtilen tabloyu okumaya devam ederseniz L soneki aslında benzer kurallara sahiptir: eğer sayı long32 bitlik durumda talep edilene uymuyorsa, derleyici size bir long longyer verecektir sadece iyi sığacak.


3
"... değişmez değeri -2147483648L ile değiştirin, açıkça imzalanan bir uzun alıyorsunuz." Hmmm, 32 bit olarak longsistemde 2147483648L, bir sığmaz longo olur, böylece long long, o zaman- ya da ben öyle - uygulanır.
chux - Monica

2
@ASH Bir int'in sahip olabileceği maksimum sayı o zaman olduğundan 0x7FFFFFFF. Kendiniz deneyin:#include <limits.h> printf("%X\n", INT_MAX);
Lundin

5
@ASH Kaynak kodundaki tamsayı değişmezlerinin onaltılık gösterimini işaretli bir sayının temel ikili gösterimi ile karıştırmayın. 0x7FFFFFFFKaynak kodda yazıldığında gerçek değeri her zaman pozitif bir sayıdır, ancak intdeğişkeniniz elbette 0xFFFFFFFF değerine kadar ham ikili sayılar içerebilir.
Lundin

2
@ASH ìnt n = 0x80000000, imzasız değişmezden işaretli bir türe dönüştürmeyi zorlar. Ne olacak derleyicinize kalmış - bu uygulama tanımlı davranıştır. Bu durumda int, işaret bitinin üzerine yazarak tüm değişmezi göstermeyi seçti . Diğer sistemlerde, türü temsil etmek mümkün olmayabilir ve tanımlanmamış davranışı çağırırsınız - program çökebilir. Eğer aynı int n=2147483648;şekilde onaltılı gösterim ile ilgili değilse, aynı davranışı elde edersiniz .
Lundin

3
-İmzasız tamsayılara unary'nin nasıl uygulandığının açıklaması biraz genişletilebilir. Her zaman (neyse ki hiçbir zaman varsayımlara güvenmediği halde) imzasız değerlerin işaretli değerlere "yükseltileceğini" veya muhtemelen sonucun tanımsız olacağını varsaymıştım. (Dürüst olmak gerekirse, bu bir derleme hatası olmalı; - 3uhatta ne anlama geliyor?)
Kyle Strand

27

0x80000000unsigned2147483648 değerine sahip bir değişmez değerdir.

Bu tekli eksi uygulanması hala size sıfır olmayan bir değere sahip işaretsiz türü verir. (Aslında, sıfır olmayan bir değer için x, sonuçta elde ettiğiniz değer 'dir UINT_MAX - x + 1.)


23

Bu tamsayı değişmezinin 0x80000000türü vardır unsigned int.

C Standardına göre (6.4.4.1 Tamsayı sabitleri)

5 Bir tamsayı sabitinin türü, değerinin temsil edilebileceği karşılık gelen listenin ilkidir.

Ve bu tamsayı sabiti, tipiyle temsil edilebilir unsigned int.

Yani bu ifade

-0x80000000aynı unsigned inttiptedir. Dahası 0x80000000, ikisinin tamamlayıcı gösteriminde aşağıdaki değeri hesaplayan aynı değere sahiptir.

-0x80000000 = ~0x80000000 + 1 => 0x7FFFFFFF + 1 => 0x80000000

Örneğin yazmak için bunun bir yan etkisi vardır

int x = INT_MIN;
x = abs( x );

Sonuç yine olacak INT_MIN.

Böylece bu durumda

bal < INT32_MIN

normal aritmetik dönüşüm kurallarına göre long long int türüne dönüştürülen işaretsiz değer 0ile karşılaştırılır .0x80000000

0'ın küçük olduğu açıktır 0x80000000.


12

Sayısal sabit 0x80000000tiptedir unsigned int. Eğer -0x800000002s iltifat matematik alır ve yaparsak, bunu elde ederiz:

~0x80000000 = 0x7FFFFFFF
0x7FFFFFFF + 1 = 0x80000000

Yani -0x80000000 == 0x80000000. Ve karşılaştırma (0 < 0x80000000)( 0x80000000imzasız olduğu için) doğrudur.


Bu, 32 bit ints varsayılır . Bu çok yaygın bir seçim olmasına rağmen, herhangi bir uygulamada intdaha dar veya daha geniş olabilir. Ancak bu durum için doğru bir analizdir.
John Bollinger

Bu OP koduyla ilgili değildir -0x80000000, imzasız aritmetiktir. ~0x800000000farklı kod.
MM

Bu benim için en iyi ve doğru cevap gibi görünüyor. @ MM iki tamamlayıcı nasıl alınacağını açıklıyor. Bu cevap özellikle negatif işaretin sayıya ne yaptığını ele almaktadır.
Ahtapot

Eksi işareti @Octopus edilir değil bu açık görünüyor olsa da, kodda neler olduğunu açıklayan değil numarası (!) İçin 2'ye tümleyenini uygulayarak -0x80000000! Aslında 2'nin tamamlayıcısı bu soru ile tamamen ilgisizdir.
MM

12

-Sayısal sabitin bir parçası olduğunu düşünürken bir karışıklık noktası ortaya çıkar .

Aşağıdaki kodda 0x80000000sayısal sabit bulunmaktadır. Türü sadece bunun üzerinde belirlenir. Daha -sonra uygulanır ve türü değiştirmez .

#define INT32_MIN        (-0x80000000)
long long bal = 0;
if (bal < INT32_MIN )

Ham süslenmemiş sayısal sabitler pozitiftir.

O ondalık ise, atanan tip bunu yapacak ilk türüdür: int, long, long long.

Sabit sekizlik veya onaltılık ise, onu tutan birinci tip alır: int, unsigned, long, unsigned long, long long, unsigned long long.

0x80000000, OP sisteminde unsignedveya türünü alır unsigned long. Her iki durumda da, imzasız bir tür.

-0x80000000Ayrıca sıfır olmayan bir değer ve kod bu karşılaştırdığında bazı işaretsiz türü olmak, bu daha büyük 0'dır long long, değerler , yani karşılaştırma 2 tarafta değiştirilmez 0 < INT32_MINgeçerlidir.


Alternatif bir tanım bu meraklı davranışı önler

#define INT32_MIN        (-2147483647 - 1)

Bir süredir fantezi diyarında yürüyelim intve unsigned48 bit.

Sonra 0x80000000sığar intve türü de öyle int. -0x80000000negatif bir sayıdır ve çıktı alınma sonucu farklıdır.

[Gerçek kelimeye dön]

Yana 0x80000000imzalı tip önce bazı imzasız türü uyan o biraz daha büyük olduğu için some_signed_MAXiçinde henüz some_unsigned_MAX, bazı imzasız türüdür.


8

C tamsayı değişmezi olabileceğini bir kuralı vardır signedya unsignedda sığar bağlıdır signedveya unsigned(tamsayı promosyon). Bir 32bit makinede değişmez 0x80000000olacak unsigned. Arasında 2'ye tümleyen -0x80000000olan 0x80000000 32 bit makinede. Bu nedenle karşılaştırma bal < INT32_MIN, C kuralına göre karşılaştırma arasında signedve unsignedöncesinde unsigned intolur long long.

C11: 6.3.1.8/1:

[...] Aksi takdirde, işaretli tamsayı türüne sahip işlenen türü, işaretsiz tam sayı türüne sahip işlenen türünün tüm değerlerini temsil edebiliyorsa, işaretsiz tam sayı türüne sahip işlenen, işlenen türüne dönüştürülür. imzalı tamsayı türü.

Bu nedenle, bal < INT32_MINher zaman true.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.