Tamsayılar (integer
ya da int), bütün programlama dillerinde bulunurlar.
Tamsayıların çeşitlerini, gücünü ve sınırlarını bilmekte yarar
var. C#'ta nasıl tanımlandığını ve kullanıldığını aşağıdaki
örnekte inceleyelim.
1 |
using System; |
2 |
class Program |
3 |
{ |
4 |
static void Main() |
5 |
{ |
6 |
int i1 = 10; |
7 |
int i2 = 20; |
8 |
int i3 = i1 + i2; |
9 |
Console.WriteLine(i3); |
10 |
} |
11 |
} |
Günümüz sayısal
bilgisayarları, ikili sayı sistemi ile çalışır ve her bilgi 0 ve
1 rakamlarından oluşan veri bitlerine dönüştürülür. İkili sayı
sisteminde her bir rakama bit denir. 1 bitlik bir veri ya 0 ya
da 1 olur, yani 2 değişik durum oluşabilir. 2 bitlik bir sayı
ise, 00, 01, 10 ya da 11 olabilir, yani 4 farklı durum
oluşabilir. Bunu basitçe hesaplamak istersek, 2^bitsayısı
şeklinde formüle edebiliriz. Mesela 32 bitlik bir veri 2^32
farklı durumda (4.294.967.296 çeşit) olabilir.
C#, int türünde bir
değişken için 32 bit (4 byte) kullanır. int, hem pozitif hem de
negatif sayıları tutabilir. Bu yüzden 32 bitlik işaretli
tamsayı da denir. İşaretli denmesinin sebebi, hem negatif
hem de pozitif sayıları tutabilmesidir. Bunu da, en soldaki bir
biti işaret için kullanarak yapar. int tipinde bir tamsayıyı
ikili sistemdeki karşılığı ile yazarsak, solundaki bit 0 ise,
sayı pozitif, 1 ise negatiftir. Eğer 32 bitin tamamı 0 ise sayı
0 olur. int türünde, bir sayı yazmak istersek, -2.147.483.648
ile 2.147.483.647 arasında bir sayı yazabiliriz.
checked unchecked
durumları
Belirtilen sınırların
dışında bir sayıyı int türünde bir değişkende tutmak istersek
taşma meydana gelir. Eğer -2.147.483.648 sayısından daha küçük
bir sayı tutmak istersek underflow, 2.147.483.647 sayısından
büyük bir sayı tutmak istersek overflow durumu oluşur. Biz genel
olarak her iki durumu da overflow olarak adlandırırız.
Bunu engellemek
mümkündür. Eğer program yazmak için key of csharp kullanıyorsak,
bunu aşağıdaki resimde görüldüğü gibi compile options menüsünden
yapabilirsiniz.
Eğer resimde
gördüğünüz Check For Arithmetic Overflow seçeneği işaretli
değilse, oluşacak taşmaları dikkate almadan işlemler
yapılacaktır ve sonuçların yanlış çıkma ihtimali yükselecektir.
Eğer, resimde görülen seçenek işaretli olursa, taşmalar dikkate
alınacak ve çalışma sırasında taşma hataları görülecektir.
Belirli bir program
bloğunun, aritmetik işlem taşmalarından muaf tutulmasını
sağlayabiliriz ya da tam tersi olarak, taşma işlemlerinde hata
oluşturacak şekilde işaretleyebiliriz. Bu işi de checked ya da
unchecked program blokları oluşturarak yaparız.
Mesela programımız
derleme sırasında aritmetik taşma işlemlerini denetleme seçeneği
aktifken derlenmiş olsun. Bu durumda programı çalıştırdığımızda
aritmetik taşma işlemi olunca program kesilecektir. Bir program
bloğunu seçelim ve checked bloğu yapalım. Taşma durumu
oluştuğunda checked bloğunun içerisindeki kodlarda oluşan
taşmalar dikkate alınmaz. Derleme sırasında aritmetik taşma
işlemlerini denetle seçeneği aktif değilken ise unchecked bloğu
için tam tersi mümkün oluyor.
Aşağıdaki örneği
aritmetik taşma işlemlerinde taşma hatası verecek şekilde
ayarlanmış olarak derlerseniz taşma durumu oluşmayacaktır ve
sonuç ekrana yazdırılacaktır. Bunu sağlayan ise unchecked
bloğudur.
1 |
using System; |
2 |
class Program |
3 |
{ |
4 |
static void Main() |
5 |
{ |
6 |
unchecked |
7 |
{ |
8 |
int A = 1000000000; |
9 |
int B = A * 20; |
10 |
Console.WriteLine(B); |
11 |
} |
12 |
} |
13 |
} |
Buraya kadar olan
kısımda tamsayı türü olarak sadece int bahsedildi ama,
tamsayılar sadece 32 bitlik işaretli tamsayılar değildir. Ama
int C#ta en çok kullanılan tamsayı türüdür. Mesela bir
etkinliğe katılan insan sayısını tutacaksanız, bunu negatif
sayılara da izin veren int türü ile yapmak zorunda değilsiniz.
İşaretsiz 32 bit tamsayıları tutabilen uint türü bir tamsayı
değişkende tutabilirsiniz. uint, 32 bitliktir, tamsayı tutar ama
0 ile 4.294.967.295 arasındaki tamsayıları tutabilir. uint
türünde de yine taşma hataları oluşabilir ve bunu yine checked
ve unchecked blokları ile muaf tutabiliriz.
Eğer yazdığınız
uygulamada, 32-bit sayılar yetmiyorsa, daha büyük ve daha küçük
sayıları kullanabilmek için long ve ulong tamsayı türlerini
kullanmalısınız. long işaretli 64-bit, ulong ise işaretsiz 64
bit tamsayıdır.
32 bitten daha küçük
sayılar tutmak için de tamsayı türleri vardır. short ve ushort
tipleri işaretli ve işaretsiz 16-bit tamsayı türleridir. sbyte
ve byte ise 8-bit işaretli ve işaretsiz tamsayı türleridir.
System isim uzayının
içerisinde Int32, Int16, Int64 gibi yapılar vardır. int, short ,
long gibi aliaslar yardımıyla da bu yapılara erişebiliriz.
Mesela int tamsayı türü aslında Int32 ismiyle System isim
uzayında bulunur ama int yazarak ulaşabiliriz. Aşağıdaki
tanımlamalardan her ikisi de doğrudur.
Aşağıdaki tabloda
.net tarafından desteklenen tamsayı türleri, .net içindeki yapı
isimleri ile alabilecekleri en küçük ve en büyük değerler toplu
olarak bulunmaktadır.
C# Tür |
.NET Yapısı |
En Küçük |
En Büyük |
sbyte |
System.SByte |
-128 |
127 |
byte |
System.Byte |
0 |
255 |
short |
System.Int16 |
-32,768 |
32,767 |
ushort |
System.UInt16 |
0 |
65,535 |
int |
System.Int32 |
-2,147,483,648 |
2,147,483,647 |
uint |
System.UInt32 |
0 |
4,294,967,295 |
long |
System.Int64 |
-9,223,372,036,854,775,808 |
9,223,372,036,854,775,807 |
ulong |
System.UInt64 |
0 |
18,446,744,073,709,551,615 |
.net ailesinde yer
alacak dillerin uyması gereken CLS (Ortak Dil Özellikleri),
bütün dillerin bir birleri ile haberleşebilmeleri için
oluşturulmuştur. .net aliesine katılacak bir dil, en azından bu
özellikleri taşımalıdır.
Yukarıda görülen
tablodaki SByte, UInt16, UInt32 ve UInt64 tamsayı veri
türleri CLS uyumlu değildir. CLS, Byte, Int16, Int32 ve
Int64 veri türlerini gereklilik olarak şart koşar ama
diğerlerini değil. Bazı diller (mesela Visual Basic.net); Byte,
Int16, Int32 ve Int64 haricindeki tam sayı türlerini
desteklemez. Eğer diğer diller tarafından da kullanılacak bir
dll yazıyorsanız, sbyte, uint16, uint32 ve uint64 veri türlerini
kullanmaktan sakınmalısınız. Yoksa, yazdığınız dll mesela Visual
Basic .net tarafından kullanılamaz.
Bir int, 32-bit
işaretli tamsayı anlamına gelir ama aynı zamanda int, System.Int32
adlı yapının bir örneğidir. Böyle olmasının getirdiği bir takım
avantajlar var. Tamsayı türlerini ifade etmekte kullandığımız
yapıların ToString ve Parse diye adlandırılan ve en çok
kullanılan yöntemleri vardır. Her birinin, MinValue ve MaxValue
adında iki tane de alanı vardır.
Alanlar da yöntemler
gibidir. Bir yapının ya da sınıfın üyesi olurlar. Ama alan daha
basittir. Bunu bir değişken ya da sabit olarak düşünebilirsiniz.
Mesela MinValue ve MaxValue alanları sabit değerlere
sahiptirler. Program içerisinde alanları sınıfınismi.alanismi
şeklinde kullanabilirsiniz. Aşağıdaki program, int tamsayı
türünün alabileceği en küçük ve en büyük sayısal değeri ekrana
yazar.
1 |
using System; |
2 |
class Program |
3 |
{ |
4 |
static void Main() |
5 |
{ |
6 |
Console.WriteLine(Int32.MinValue); |
7 |
Console.WriteLine(Int32.MaxValue); |
8 |
} |
9 |
} |
Aynı şekilde diğer
tamsayı türlerinin de alabileceği en küçük ve en büyük sayısal
değerleri de ekrana yazdırabilirsiniz.
Integer literalleri,
hexadecimal notasyonda da yazabilirsiniz. Hexadecimal sayılar,
0x ya da 0X ile başlar ve devamında 16lı sistemde yazılmış bir
sayı gelir.
Sınırları aşmamak
kaydıyla tamsayı veri türlerini aritmetik işlemlerde birlikte
kullanabilirsiniz.
ushort US = 45867; |
int I = 523; |
int S = US + I; |
Yukarıdaki örnekte
toplama işlemi meydana gelmeden önce, C# otomatik olarak ushort
yapıdaki sayıyı 32-bit işaretli tamsayıya dönüştürür. Çünkü
toplama gibi aritmetik işlemler ancak aynı tür iki sayı arasında
meydana gelebilir. Daha sonra toplama işlemi yapılır ve sonuç
yine 32-bit işaretli bir tamsayıdır.
Burada dönüştürme
işlemi programcının müdahalesi olmadan C# tarafından
yapılmıştır. Bu tür dönüşümlere kapalı dönüştürme denir.
Eğer derleme
evresinde sabit kalacak bir tamsayı sabiti tanımlamışsak, ve
tamsayı 8-bit ya da 16-bit bir türdeyse, tanımlanan sabit
geçerlidir ama C# onu 32-bit işaretli tamsayıya dönüştürerek
derler.
Birden çok işlemi
içine alan karmaşık bir işlem sırasında, adım adım dönüştürme
işlemleri yapılır. Bir atama işlemi sırasında operatörün
sağındaki ifadenin türü solundaki ifadenin türüne kapalı
dönüştürme ile dönüştürülür. Dönüştürme işlemleri asıl işlemden
daima önce yapılır.
Eğer bir int ile bir
uint sayı arasında işlem yapılacaksa, C# önce her ikisini de
long türüne dönüştürür. Çünkü, hem int hem de uint türünü
kapsayan tamsayı türü longdur. Bu durumda aşağıdaki örnek
çalışmayacaktır:
1 |
using System; |
2 |
class Program |
3 |
{ |
4 |
static void Main() |
5 |
{ |
6 |
uint UI = 45867; |
7 |
int I = 523; |
8 |
int A = I + UI; // Bu satırda
derleme hatası oluşur! |
9 |
} |
10 |
} |
Hatanın nedeni,
işlemin sonucununu int türü bir tamsayıya atamaya çalışmamızdır.
Çünkü UI, bir uint türü sayıdır; I ise int türündedir. Bu iki
türün bir işlemde kullanılması sonucu oluşacak sonuç long
türünde olacaktır ve ancak long türü bir değişkene atanabilir.
Oysa yukardaki örnekte sonuç , int türünde bir değişkene
atanmaktadır.
Bazen, C#
derleyicisinin kapalı dönüştürme yapmak istemediği bir işlemi
yapma istersiniz. Bu işlemi yapmak güvenlidir ama C# derleyicisi
buna müsaade etmez. O zaman, açık dönüştürme işlemi yaparsınız.
Aşağıdaki örnekte bu şekilde açık dönüştürme yapılmıştır ve C#
derleyicisi, bu örneği derler.
1 |
using System; |
2 |
class Program |
3 |
{ |
4 |
static void Main() |
5 |
{ |
6 |
uint UI = 45867; |
7 |
int I = 523; |
8 |
int A = (int) (I + UI); |
9 |
Console.WriteLine(A); |
10 |
} |
11 |
} |
Her bir değişkeni tek
tek de açık dönüştürme işlemine tabi tutabilirsiniz. Yukarıdaki
örneği incelerseniz, UI adlı değişken uint türündedir ve sadece
onu dönüştürerek de int türündeki A değişkenine sonucu
atayabilirsiniz.
1 |
using System; |
2 |
class Program |
3 |
{ |
4 |
static void Main() |
5 |
{ |
6 |
uint UI = 45867; |
7 |
int I = 523; |
8 |
int A = I + (int) UI; |
9 |
Console.WriteLine(A); |
10 |
} |
11 |
} |
Toplama işlemi
yapılmadan önce UI değişkeni 32-bit işaretli tamsayı türüne
dönüştürüldü ve işlem iki tane 32-bit işaretli tamsayı arasında
yapıldı.
Bu tür dönüştürmeye
daha çok type casting (tür dönüştürme) de denir. Bundan sonraki
yazılarda sadece casting olarak kullanacağız.
Casting işlemi
sırasında bit kayıpları da oluşabilir. Mesela bir 32-bit
işaretli sayıyı 8-bit işaretli sayıya dönüştürecek olursak 8-bit
işaretli tamsayı snırılarını aşan kısım ihmal edilecektir. Eğer
aritmetik taşma işlemlerini denetleme seçeneği aktifse çalışma
evresinde taşma istisnası oluşacaktır.
Casting işlemi birçok
kolaylık sağlar ama suiistimale de açıktır. Kullanırken dikkat
etmek gerekir. Oluşabilecek istisnaları yakalayacak yordamlar
yazmak gerekir ki, bu işlemin nasıl yapacağını başka bir yazıda
ele alacağız.