Neden en negatif int değeri belirsiz işlev aşırı yüklemeleri hakkında bir hataya neden oluyor?


91

C ++ 'da fonksiyon aşırı yüklemeyi öğreniyorum ve şuna rastladım:

void display(int a)
{
    cout << "int" << endl;
}

void display(unsigned a)
{
    cout << "unsigned" << endl;
}

int main()
{
    int i = -2147483648;
    cout << i << endl; //will display -2147483648
    display(-2147483648);
}

Anladığım kadarıyla, intaralıkta verilen herhangi bir değer (benim durumumda int4 bayttır) arayacak display(int)ve bu aralığın dışındaki herhangi bir değer belirsiz olacaktır (çünkü derleyici hangi işlevi çağıracağına karar veremez). intMin değeri haricindeki tüm değerler aralığı için geçerlidir, yani -2147483648derlemenin hata ile başarısız olduğu durumlarda

aşırı yükleme çağrısı display(long int)belirsizdir

Ama aynı değeri intalıp basmak değeri verir 2147483648. Bu davranışla tam anlamıyla kafam karıştı.

Bu davranış neden yalnızca en negatif sayı geçildiğinde gözlemleniyor? (A shortile birlikte kullanıldığında davranış aynıdır -32768- aslında, negatif sayı ve pozitif sayının aynı ikili gösterime sahip olduğu her durumda)

Kullanılan derleyici: g ++ (GCC) 4.8.5


4
Int'in minimum değeri "bir derleyici hatası atıyor". Ne hatası? Bunu soruya dahil etmelisiniz
Justin

11
Görüyorum call of overloaded ‘display(long int)’ is ambiguous.
crashmstr

6
İlişkili değil, ancak derleyiciyi güncellemelisiniz. Zaten GCC 7.1 var.
HolyBlackCat

4
İşte benim tahminim: typeof(-2147483648) != int. Gerçek şu ki 2147483648, ki bu bir için çok büyük int, yani bu a longve reddediliyor
Justin

3
İlginç bir şekilde, g ++ (en azından 6.4 ve 7.1) int j{-2147483648};bunun daralan bir dönüşüm olduğundan şikayet etmiyor . Neredeyse başlı başına bir soruya değer. Muhtemelen başlatmada daraltılacak long longgibi (örneğin) constexpr değerlerine izin vermekle ilgilidir 2147483647LL.
Toby Speight 03

Yanıtlar:


145

Bu çok ince bir hatadır. Gördüğünüz şey, C ++ 'da negatif tamsayı değişmezlerinin olmamasının bir sonucudur. Biz [lex.icon] bakarsak biz bir olsun tamsayı değişmezi ,

tamsayı-değişmez
        ondalık-değişmez tamsayı-sonek tercih
        [...]

ondalık olabilir ,

ondalık-değişmez:
        sıfır olmayan
        ondalık-değişmez ' tercih basamağı

burada basamaklı bir [0-9]ve sıfır olmayan bir basamaklı olduğu [1-9]ve son ek kısmen biri olabilir u, U, l, L, ll, ya da LL. Burada hiçbir yerde -ondalık düz bilginin bir parçası olarak yer almaz .

§2.13.2'de ayrıca:

Bir tamsayı değişmez değeri, nokta veya üs parçası olmayan, değeri belirlenirken göz ardı edilen isteğe bağlı tek tırnak işaretlerinin ayrıldığı bir rakam dizisidir. Bir tamsayı hazır bilgisinin tabanını belirten bir öneki ve türünü belirten bir soneki olabilir. Rakam dizisinin sözcüksel olarak ilk rakamı en önemli olanıdır. Bir ondalık tamsayı değişmez değeri (on tabanı), 0 dışında bir rakamla başlar ve bir ondalık rakam dizisinden oluşur.

(vurgu benim)

Hangi vasıta -içinde -2147483648tekli olduğunu operator -. Bu -2147483648aslında olarak değerlendirildiği anlamına gelir -1 * (2147483648). Yana 2147483648senin için kimsenin bu kadar çok olan intbir terfi edilir long intve belirsizlik eşleşen değil geliyor.

Bir tür için minimum veya maksimum değeri taşınabilir bir şekilde almak istiyorsanız şunları kullanabilirsiniz:

std::numeric_limits<type>::min();  // or max()

2
-2147483647 - 1olumsuz bir değişmez ifade olarak uyarı olmadan da çalışır
Cœur

2
Veya INT_MINen az ayrıntılı seçenek için. Yine de daha az genel.
MSalters

@NathanOliver, bana bu durumu açıklar mısın lütfen display(2147483649);? Bu durumda neden işaretsiz int func çağrılamıyor? ve neden arg'yi 2147483649işaretsiz int yerine long int olarak ele alıyor ?
sonsuz döngü

2
değişmezleri gitmek tamsayı ondalık @infiniteloop intiçin long intiçin long long int. Eğer kullanmadıkça bir ondalık sabitin için işaretsiz türü asla wlll u/ Ueki.
NathanOliver

2
Bu örnekte evet. Aramak için display(unsigned a)ya display(1234u);ya da ya display(static_cast<unsigned>(1234));daunsigned foo = 1234; display(foo);
NathanOliver

36

İfade -2147483648aslında -operatörü sabite uygulamaktadır 2147483648. Platformunuzda intdepolanamaz 2147483648, daha büyük bir türle temsil edilmelidir. Bu nedenle, ifade daha büyük işaretli bir tür -2147483648olarak çıkarılır .signed intsigned long int

longDerleyici için bir aşırı yükleme sağlamadığınız için , her ikisi de eşit derecede geçerli olan iki aşırı yükleme arasında seçim yapmaya zorlanır. Derleyiciniz belirsiz aşırı yüklemeler hakkında bir derleyici hatası vermelidir.


4

Başkalarının cevaplarını genişletmek


OP'nin neden karıştırıldığını açıklığa kavuşturmak için önce : aşağıdaki signed intikili temsilini düşünün 2147483647.

En büyük imzalı int




Sonra, bu numaraya tane ekleyin : Başka bir verme signed intait -2147483648(OP kullanımına istediği) En küçük imzalı int



Son olarak: 32 bite açıkça sığdığı için OP'nin -2147483648a long intyerine a'ya derlendiğinde neden karıştırıldığını görebiliriz signed int.

Geçerli cevaplar söz Fakat, tekli operatör ( -) uygulanır sonra çözme 2147483648bir olan long intve 32 bit yeterlidir DEĞİLDİR.

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.