|
C#'ta Gösterici(Pointer) Kullanmak - II |
|
Gönderiliyor lütfen bekleyin... |
|
|
C#'ta göstericilerin
kullanımına yönelik ilk yazıda göstericilere giriş yapmıştık, C#'ta göstericilerin
kullanımını 3 yazılık bir seri halinde anlatmayı düşündüm. Bu yazıda gösterici
aritmetiğini ve fixed anahtar sözcüğünün kullanımını öğreneceğiz.
Göstericilerin
adres bilesenlerine sabit tamsayi degerleri ekleyebiliriz, ayni sekilde göstericilerin
adres bileseninden sabit bir tamsayi degerini çikarabiliriz. Ancak göstericilere
uygulanan bu toplama ve çikarma islemleri biraz farklidir. Göstericilere sabit
degerlerin eklenmesi yada bir degerin göstericiden çikarilmasi göstericideki
tür bileseni ile yakindan ilgilidir. Bir göstericinin degerini bir artirmak
göstericinin adres bilesenini, göstericinin türünün içerdigi byte sayisi kadar
artirmak demektir. Ayni kural çikarma islemi içinde geçerlidir. Örnegin int
türünden bir gösterici ile 1 sayisini toplamak göstericinin adres bilesenini
4 artirmak anlamina gelir. Çünkü int türü 4 byte büyüklügündedir. Ayni sekilde
int türden bir göstericiden 1 sayisini çikarmak göstericinin adres bilesenini
4 eksiltmek anlamina gelir. Göstericilerle yapilan bu tür aritmetik islemlerin
tamamina gösterici aritmetigi denilmektedir.
Gösterici aritmetigini
daha yakindan görmek için asagidaki programi yazin ve sonucunu inceleyin.
using
System;
class
Gosterici
{
unsafe static void Main()
{
int*
ptr1 = (int*)500;
char*
ptr2 = (char*)500;
double*
ptr3 = (double*)500;
byte*
ptr4 = (byte*)500;
ptr1
+= 2;
ptr2 += 5;
ptr3 += 2;
ptr4 += 6;
Console.WriteLine((uint)ptr1);
Console.WriteLine((uint)ptr2);
Console.WriteLine((uint)ptr3);
Console.WriteLine((uint)ptr4);
}
}
|
Programi /unsafe
argümani ile derleyip çalistirdigimizda asagidaki ekran görüntüsünüz elde ederiz.
Programda Main() metodunun unsafe olarak isaretlendigine dikkat edin.
508
510
516
506
Programin çiktisindan
da görüldügü üzere int türden bir göstericiye 2 sayisini eklemek göstericinin
adres bilesenini 2*4=8 kadar artirmistir. Ayni sekilde char türden bir göstericiye
5 degerini eklemek göstericinin adres bilesenini 5*2 =10 kadar artirmistir.
Toplama yerine çikarma islemi yapilmis olsaydi bu sefer ayni oranda adres bileseni
eksiltimis olacakti.
Göstericiler üzerinde
sadece tamsayilarla aritmetik islemler yapilabilir. Göstericiler ile asagidaki
aritmetik operatörleri kullanabiliriz.
+ , - , -- , ++
, -=, +=
void göstericilerde
herhangi bir tür bilgisi olmadigi için bu tür göstericiler üzerinde aritmetik
islemler yapilamaz. Çünkü void türünden bir göstericiye örnegin 1 eklemek istedigimizde
göstericinin adres bileseninin kaç byte ötelenecegi belli degildir.
Göstericiler üzerinde
yapilabilecek diger önemli islemde iki göstericinin birbirinden çikarilmasidir.
Gösterici türleri ayni olmak sartiyla iki göstericiyi birbirinden çikarabiliriz.
Ancak iki göstericinin çikarilmasi sonucunda üretilen deger bir gösterici türü
degildir. Iki gösterici arasindaki fark, adres bilesenlerinin sayisal farkinin
gösterici türlerinin büyüklügünden kaç adet byte miktari edecegidir. Diger bir
deyisle adres bilesenlerinin sayisal farki alinip gösterici türünün byte miktarina
göre bir deger belirlenir. Iki göstericinin farki long türden bir deger üretir.
Iki göstericinin farkina örnek verecek olursak, int türden 5008 adres ile int
türden 5000 adresinin farki (5008-5000) % sizeof(int) tir. Yani sonuç long türden
2 dir. Asagidaki programi yazarak sonucu görebilirsiniz.
using
System;
class
Gosterici
{
unsafe static void Main()
{
int*
ptr1 = (int*)500;
int*
ptr2 = (int*)508;
long
fark=ptr2 - ptr1;
Console.WriteLine(fark);
}
}
|
Diger bir ilginç
nokta iki göstericinin adres bilesenlerinin farki gösterici türlerinin büyüklügünün
tam kati olmadiginda görülür. Örnegin ptr2 göstericisini tanimlanmasini
seklinde degistirdiginizde
bu sefer ekrana 1 yazdigini görürsünüz. Burdan çikarmamiz gereken sonuç iki
göstericinin farki adres bilesenlerinin sayisal farkinin olmamasidir.
Dikkat: Iki göstericinin
farki long türden bir deger üretir. Bu yüzden iki göstericinin farki açikca
bir tür dönüsümü yapilmadikça long türünden küçük türden olan degiskenlere atanamaz.
Göstericiler ile
kullanilabilecek diger operatörler ise ==, < ve > gibi karsilastirma operatörleridir.
Bu operatörler iki göstericinin adres bilesenini karsilastirip ture yada false
degeri üretirler. Karsilastirma operatörleri göstericiler için çok istisnai
durumlar disinda anlamli degildir. Bu istisna durumlardan biri göstericileri
kullanarak dizi islemleri yaptigimizda görülür.
fixed
Anahtar Sözcügü
Bildiginiz gibi
C#' ta tanimladigimiz referans degiskenleri heap bellek bölgesindeki adresler
temsil ederler. Ancak biz adresler yerine nesnenin ismini kullaniriz. Gereksiz
bilgi toplayicis(garbage collector) bellek optimizasyonui açisindan heap bellek
bölgesindeki nesnelerin yerlerini her an degistirebilir. Bu yer degisiminden
bizim haberimiz olmaz, çünkü nesnenin yeri degistigi anda bu nesneye referans
olan stack bellek bölgesindeki degiskenin adres bileseni de degistirilir. Dolayisiyla
biz ayni referans ile farkli bellek bölgesini istegimiz disinda kullanmis oluruz.
Ancak bazi durumlarda gereksiz nesne toplayicisina bir nesnenin adresini degistirmemesi
için ikna etmek durumunda kaliriz. Bu, özellikle sinif nesnelerinin üye elemanlarindan
birinin adresi ile islem yapmamiz gerektigi durumlarda karsimiza çikar. Bir
degiskenin adresinin belirlenen bir faaliyet alani boyuncu degismeden kalmasi
için bunu gereksiz nesne toplayicisina bildirmemiz gerekir. Bunun için fixed
anahtar sözcügü kullanilir.
Zaten fixed anahtar sözcügünü kullanmadan referans türünden nesnelerin üye elemanlarinin
adreslerini elde etmemiz mümkün degildir. Üye elemanlarinin adreslerini elde
edemedigimiz bu tür nesnelere managed type(yönetilen tip) denilmektedir. Buna
göre siniflar managed type kapsamina girmektedir.
Asagidaki programda
ManagedType isimli sinifin int türden olan x elemaninin adresi bir göstericiye
atanmak isteniyor.
using
System;
class
ManagedType
{
public int x;
public
ManagedType(int x)
{
this.x
= x;
}
}
class
Gosterici
{
unsafe
static void Main()
{
ManagedType
mt = new ManagedType(5);
int*
ptr1 = &(mt.x);
}
}
|
ManagedType sinifinin
x elemani deger tipi olmasina ragmen mt nesnesi üzerinden x degiskeninin adresi
elde edilememektedir. Çünkü x degiskeninin adresi gereksiz nesne toplayicisi
tarafindan her an degistirilebilir. Eger yukaridaki kod geçerli olmus olsaydi
x degiskeninin adresi degistigi anda ptr1 göstericisi nereye ait oldugu bilinmeyen
bir adres bilgisi tasiyor olacakti. x degiskeninin bir blok içerisinde sabit
adreste olmasini istiyorsak asagidaki gibi fixed anahtar sözcügünü kullanmaliyiz.
using
System;
class
ManagedType
{
public int x;
public
ManagedType(int x)
{
this.x
= x;
}
}
class
Gosterici
{
unsafe
static void Main()
{
ManagedType
mt = new ManagedType(5);
fixed(int*
ptr1 = &(mt.x))
{
//x'in
adresi bu blokta asla degismez.
}
}
}
|
Yukaridaki fixed
ile isaretlenmis blokta x'in adresinin degismeyecegi garanti altina alinmistir.
Birden fazla degiskeni fixed olarak isaretlemek için asagidaki gibi bir kullanim
geçerli kilinmistir.
ManagedType
mt1 = new ManagedType(5);
ManagedType mt2 = new ManagedType(5);
fixed(int*
ptr1 = &(mt1.x))
fixed(int* ptr2
= &(mt2.x))
{
//x'in adresi bu blokta
asla degismez.
}
|
Öte yandan bir
fixed bildirimi içinde adreslerinin degismesini istemedigimiz elemanlari virgül
ile ayirarak asagidaki gibi de bildirebiliriz.
ManagedType
mt1 = new ManagedType(5);
ManagedType mt2 = new ManagedType(5);
fixed(int* ptr1
= &(mt2.x), ptr2 = &(mt2.x))
{
//x'in adresi bu blokta asla degismez.
}
|
Göstericilerle
ilgili son yazıda, yapı göstericileri, göstericiler ile dizi işlemleri ve stackalloc
ile dinamik alan tahsisatı yapma gibi konuları inceleyeceğiz.
Makale:
C#'ta Gösterici(Pointer) Kullanmak - II C#, Visual C# ve .NET Sefer Algan
|
|
|
-
-
Eklenen Son 10
-
Bu Konuda Geçmiş 10
Bu Konuda Yazılmış Yazılmış 10 Makale Yükleniyor
Son Eklenen 10 Makale Yükleniyor
Bu Konuda Yazılmış Geçmiş Makaleler Yükleniyor
|
|