ProgramlamaC Sistem Programcısı

Farklı işaretli (signed/unsigned) sayılar arasında tür dönüşümünün özelliklerini açıklayın, hangi tuzaklarla karşılaşılabilir, işaretli/işaretsiz türlerin aritmetiği ve karşılaştırılması sırasında öngörülemeyen sonuçları nasıl önleyebiliriz ve bu, programların taşınabilirliğini nasıl etkiler?

Hintsage yapay zeka asistanı ile mülakatları geçin

Cevap

C'de otomatik tür dönüşümü "alışılmış aritmetik dönüşümler" ilkesine göre çalışır. Farklı işaretlerde (signed/unsigned) tanımlanmış sayıların bir ifadede yer alması durumunda, şu kurallar doğrultusunda bir dönüşüm gerçekleşir:

  • Eğer bir operand unsigned ve diğeri signed ise, signed otomatik olarak unsigned'a dönüştürülür.
  • Bu, özellikle karşılaştırma veya aritmetik işlemler sırasında beklenmedik taşmalar ile sonuçlanabilir.
  • Türlerin boyutu da önemlidir: Eğer unsigned daha büyük bit genişliğine sahipse, signed unsigned'a dönüştürülür.

Tehlikeli aritmetik örneği:

int a = -1; // signed unsigned int b = 1; printf("%d\n", a < b); // her zaman false, çünkü a çok büyük bir unsigned'a dönüştürülür

Sonuç: -1, unsigned'a dönüştüğünde çok büyük pozitif bir sayıya dönüşür.

Hatırlanması gerekenler:

  • Eğer işaretle ilgili bir karışıklık olabilirse her zaman açıkça türleri dönüştürün.
  • Türlerin boyutlarına (int, long, uint32_t vb.) dikkat edin, böylece dönüşümler öngörülebilir şekilde gerçekleşir.
  • Özellikle sınır kontrolleri ve aritmetik işlemlerinde işaretli ve işaretsiz değişkenler ile olan mantığı ayırın.

Aldatıcı soru

Soru: (int)(unsigned)-1 ifadesi ne sonuç verecek?

Beklenen yanlış cevap: "-1, çünkü -1 ve int'e dönüştürüyoruz."

Doğru cevap: (unsigned)-1 ifadesinde önce -1'in unsigned'a dönüşümü (32 bit platformda bu 0xFFFFFFFF olur) gerçekleşir, ardından tekrar signed int'e dönüştürülür. Bu, uygulanabilirliğe bağlıdır, ancak çoğu zaman bu tekrar -1 (eğer iki's tamamlayıcısı kullanılıyorsa) olur. Ancak, doğru söylemek gerekirse: Sonuç, işaretli sayıların temsil standartlarına bağlıdır, ancak çoğu uygulamada sonuç -1 çıkacaktır.

Örnek:

int x = (int)(unsigned)-1; // x == -1 çoğu platformda

Konuyla ilgili bilgi eksikliğinden kaynaklanan gerçek hata örnekleri


Hikaye

Dize işleyicisinde boyut karşılaştırma fonksiyonu kullanılmıştır: Eğer dize uzunluğu negatif olabiliyorsa, program hata bildiriyordu. Ancak, uzunluk size_t (unsigned) türündeydi ve if(length < 0) kodu her zaman false döndürdüğü için bu sonsuz döngüye ve bellek taşmasına yol açmıştır.


Hikaye

Protokolün ayrıştırılması sırasında, ağ paketleri alanları unsigned olarak, yerel değişkenler ise signed olarak tutuluyordu. Bazı değerlerin işlenmesinde unsigned taşması nedeniyle paket uzunluğu hesaplamaları hatalı çıkmış ve bu durum bir tampon taşması zafiyetine yol açmıştır.


Hikaye

Günlüklerde tarih karşılaştırma modülü tarihi unsigned int olarak saklıyordu, ancak tarih aralığını int olarak arıyordu. Beklenen istisnanın tetiklenmesi yerine bazı sınır değerler kayıtların yanlış filtrelenmesine ve önemli günlüklerin kaybolmasına yol açtı.