ÜYE GİRİŞİ  
Kullanıcı adı :
Parola :
 
  Üye Ol!  
 
 
ANASAYFA HABERLER ASP ASP.NET PHP C/C++ C# JAVA ARAMA İLETİŞİM

ASP.NET
  Expression Web ile Site Yapimi
  Web Server Control Yazmak - 2
  Web Server Control Yazmak - 1
  Sql Cache Dependency
  Web Kontrollerini Etkin Kullanmak
  Xslt Kullanımı
  Bir Web Sayfasının Anatomisi
  Client Script Callback
  Login Kontrollerinin Kullanımı

ASP
  ASP Nesneleri
  29 Soruda ASP
  Matematiksel Uygulamalar
  Select Case Yapısı 2
  Fonksiyon(Function) Kullanımı
  IF-ELSE...
  GET Yöntemi ile İşlemler
  Asp de Tarih ve Saat fonksiyonları
  While-wend döngüsü

C Sharp (C#)
  C#'da klasör işlemleri
  C# ile diziyi tersine çevirme
  Çok Kullanılan Kontroller-1
  C# ile Kronometre Uygulaması
  Overloaded Metotların Gücü
  C# ile XMLQuery Örneği
  Rastgele Kod Üretimi
  Kontroller ile Çalışma
  Düzenli İfadeler Nedir?

DUYURULAR

F# Microsoft dil teknolojisinin en yeni üyesi.Common Lisp-Slime-Emacs tarzında etkileşimli,kıvrak,sonuçların anında görülebildiği ve yüksek performanslı bir betik dil olarak özetleyebiliriz bu yeni dili. devamı >>


Türk Telekom'dan yapılan yazılı açıklamada, Telekomünikasyon Kurumu'nun onayladığı yeni tariflerin, internet servis sağlayıcı şirketlerin maliyetlerini önemli oranlarda düşüreceği aktarıldı. devamı >>


C#'ta Gösterici(Pointer) Kullanmak - III | Asundinay.Com
C Sharp (C#) İçin Eklenmiş Makale
 Makale Adı  :  C#'ta Gösterici(Pointer) Kullanmak - III
 Yayın Tarihi :  12.08.2007 14:23:56 Ekleyen :  Kazım Aydın Okunma : 199

#'ta göstericilerin kullanımı ile ilgili yazı dizisinin son bölümü olan bu yazıda gösericiler ile dizi islemlerinin nasıl yapıldıgi, stackalloc ıle dinamik bellek tahsısatının yapılmasını ve son olarak yapı(struct) göstericilerinin kullanılışını inceleyeceğiz.

Göstericiler ile Dizi İşlemleri

C#'ta tanımladıgımız diziler System.Array sınıfi türündendir. Yani bütün diziler managed type kapsamına girerler. Bu yüzden tanımladıgımız bir dizinin herhangi bir elemanının adresini fixed bloğu kullanmadan bir göstericiye atayamayız. Bu yüzden göstericiler ile dizi islemleri yaparken ya fixed bloklari kullanıp gereksiz nesne toplayıcısını uyarmalıyız yada stackalloc anahtar sözcüğünü kullanarak kendimiz unmanaged type(yönetilemeyen tip) dizileri olusturmalıyız. Her iki durumuda birazdan inceleyeceğiz.

Bildiğiniz gibi dizi elemanları bellekte ardışıl bulunur. O halde bir dizinin elemanlarını elde etmek için dizinin ilk elemanının adresini ve dizinin boyutunu bilmemiz yeterlidir. System.Array sınıfı dizilerinin elemanlarina göstericiler yardimiyla rahatlıkla ulasabiliriz. Bunun için ilk olarak fixed ile isaretlenmis bir blok içerisinde dizinin ilk elemanının adresini bir göstericiye atamalıyız. Ardından göstericinin degerini bir döngü içerisinde birer birer artırdıgımızda her döngüde dizinin bir sonraki elemanina ulaşmış oluruz. Dizilere bu sekilde erisebilmemizi sağlayan ise gösterici aritmetiğidir. Tabi dizilerin elemanlarının bellekte ardışıl bulunması da bu işlemi bu şekilde yapmamızı sağlayan ilk etkendir.

Şimdi yönetilen türden(managed) bir dizinin elemanlarına nasıl eriştiğimizi bir örnek üzerinde inceleyelim.

using System;

class Gosterici
{
   unsafe static void Main()
   {

      int[] a = {1,2,3,4};

      fixed(int* ptr = &a[0])
      {
         for(int i=0; i<a.Length; ++i)
            Console.WriteLine(*(ptr+i));
      }
   }
}

Programı derlediğinizde dizinin elemanlarının

1
2
3
4

seklinde ekrana yazdırıldığını görürsünüz. (programı derlerken /unsafe argümanını kullanmayı unutmayın). Programın kritik noktası

fixed(int* ptr = &a[0])

satırı ile dizinin ilk elemanının adresinin elde edilmesidir. Zira dizinin diğer elemanlarıda sırayla ilk elemandan itibaren her eleman için adres değeri 4 byte artacak şeklindedir. Diger önemli nokta ise for döngüsü içindeki

*(ptr+i)

ifadesidir. Bu ifade her döngüde ptr göstericisinin adres bileşeni, döngü degişkeni kadar artırılıyor, sözgelimi döngü degişkeni 1 ise ptr'nin adres bileşeni 4 artırılıyor. Bu da dizinin ikinci elemanının bellekte bulunduğu adrestir. İçerik operatörü ile bu adrese erişildiğinde ise dizinin elemanı elde edilmis olur.

Gösterici dizileri ile ilgili diğer önemli nokta göstericilerin indeksleyici gibi kullanılabilmesidir. Örneğin yukarıdaki örnekte bulunan

*(ptr+i)

ifadesini

ptr[i]

şeklinde degiştirebiliriz. Burdan aşağıdaki eşitlikleri çıkarabiliriz.

*(ptr+0) == ptr[0]
*(ptr+1) == ptr[1]
*(ptr+2) == ptr[2]
*(ptr+3) == ptr[3]

Dikkat: ptr[i] bir nesne belirtirken (ptr+i) bir adres belirtir.

Dizilerin isimleri aslinda dizilerin ilk elemanının adresini temsil etmektedir. Örneğin aşagıdaki programda bir dizinin ilk elemanının adresi ile dizinin ismi int türden bir göstericiye atanıyor. Bu iki göstericinin adres bileşenleri yazdırıldıgında sonucun aynı olduğu görülmektedir.

using System;

class Gosterici
{

   unsafe static void Main()
   {
      int[] a = {1,2,3,4};

      fixed(int* ptr1 = a, ptr2 = &a[0])
      {
         Console.WriteLine((uint)ptr1);
         Console.WriteLine((uint)ptr2);
      }
   }
}

Programı derleyip çalıştırdığınızda ekrana alt alta iki tane aynı sayının yazıldığını görürsünüz.

Yönetilen(managed) tiplerle çalışmak her ne kadar kolay olsa da bazı performans eksiklikleri vardır. Örneğin bir System.Array dizisinin bir elemanına erişmek ile stack bölgesinde olusturacagımız bir dizinin elemanına ulaşmamız arasında zaman açısından büyük bir fark vardır. Bu yüzden yüksek performanslı dizilerle çalışmak için System.Array sınıfının dışında stack tabanlı diziler oluşturmamız gerekir. Stack tabanlı diziler yönetilemeyen dizilerdir. Bu yüzden bu tür dizileri kullanırken dikkatli olmalıyız. Çünkü her an bize tahsis edilmeyen bir bellek alanı üzerinde islem yapiyor olabiliriz. Ancak yönetilen dizilerde dizinin sınırlarını aşmak mümkün degildir. Hatırlarsanız bir dizinin sınırları aşılınca çalışma zamanında IndexOutOfRangeException istisnai durumu meydana geliyordu. Oysa stack tabanlı dizilerde dizinin sınırları belirli degildir ve tabiki dizinin sınırlarını aşmak kısıtlanmamıştır. Eğer dizinin sınırları aşılmışsa muhtemelen bu işlem bir hata sonucu yapılmıştır. Hiçbir programcı kendisine ait olmayan bir bellek alanında islem yapmamalıdır. Aksi halde sonuçlarına katlanması gerekir.

Stack tabanlı diziler stackalloc anahtar sözcüğü ile yapılır. stackalloc bize istediğimiz miktarda stack bellek bölgesinden alan tahsis eder. Ve tahsis edilen bu alanın başlangıç adresini geri döndürür. Dolayısıyla elimizde olan bu baslangıç adresi ile stackalloc ile bize ayrılmış olan bütün bellek bölgelerine erişebiliriz. stackalloc anahtar sözcüğünün kullanımı aşağıdaki gibidir.

int * dizi = stackalloc int[10];

Bu deyim ile stack bellek bölgesinde 10*sizeof(int) = 40 byte'lık bir alan programcının kullanmasi için tahsis edilir. Bu alan, dizinin faaliyet alanı bitinceye kadar bizim emrimizdedir. Tahsis edilen bu 40 byte büyüklüğündeki bellek alanının ilk byte'ının adresi ise int türden gösterici olan dizi elemanına aktarılır. Dolayısyla dizi göstericisi ile içerik operatörünü kullandığımızda bize ayrılan 10 int'lik alanın ilk elemanına erişmiş oluruz.

! stackalloc ile tahsis edilen bellek alanlarının ardışıl olması garanti altına alınmıştır.

stackalloc ile alan tahsisatı yapılır. Ancak alan tahsisatı yapılan bellek bölgesi ile ilgili hiçbir islem yapılmaz. Yani yukaridaki deyim ile, içinde tamamen rastgele değerlerin bulundugu 40 byte'lık bir alanımız olur. Bu alandaki değerlerin rastgele degerler olduğunu görmek için asagidaki programı yazın.

using System;

class Gosterici
{

   unsafe static void Main()

   {
      int * dizi = stackalloc int[10];

      for(int i=0; i<10;++i)
         Console.WriteLine("*(dizi+{0}) = {1}",i,dizi[i]); }
   }
}

! Yukarıdaki programda bir gösterici ile bellekteki ardışıl bölgelere indeksleyici operatörü ile nasıl eriştiğimize dikkat edin.

Programı /unsafe argümani ile beraber derleyip çalıştırdıktan sonra aşağıdaki ekran görüntüsünü elde etmeniz gerekir. Tabi bu değerler rastgele oldugu için sizdeki görüntü tamamen farklı olacaktır. Rastgele değerden kasıt çalışma zamanında Random gibi bir sınıfin kullanılıp rastgele bir sayı üretilmesi değildir. Burdaki sayılar daha önce çalışmiş olan programlardan kalan çöp değerlerdir.

 

C:\Programlar\StackAlloc
*(dizi + 0) = 0
*(dizi + 1) = 1244236
*(dizi + 2) = 1243328
*(dizi + 3) = 1350496
*(dizi + 4) = 124334
*(dizi + 5) = 1287174
*(dizi + 6) = 1243328
*(dizi + 7) = 0
*(dizi + 8) = 0
*(dizi + 9) = 1243404

C:\Programlar\StackAlloc

 

Console.WriteLine("*(dizi+{0}) = {1}",i,dizi[i]);

satırındaki

dizi[i]

yerine

*(dizi + i)

yazmamız herhangi birşeyi değiştirmezdi. Daha önce bu iki kullanımın eşdeğer olduğunu belirtmiştik.

Stack tabanli dizilerle ilgili bilinmesi gereken en önemli nokta dizinin sınırlarının aşılması ile ilgilidir. Daha önceden de denildiği gibi stack tabanli dizilerin sınırları aşıldığında herhangi bir uyarı verilmez. Elbetteki program başarıyla derlenir ancak çalımma zamanında bize ait olmayan bir adresin içeriğini degiştirmiş oluruz ki bu da bir programcının başina gelebilecek en tehlikeli durumdur. Örneğin aşağidaki programda stackalloc ile 10 int türünden nesnelik alan tahsis edilmektedir. Buna rağmen istediğimiz kadar alanı kullanabiliyoruz.

using System;

class Gosterici
{

   unsafe static void Main()

   {
       int * dizi = stackalloc int[10];

      for(int i=0; i<50;++i)
          *(dizi+i) = i;
   }
}

stackalloc ile oluşturacağımız dizilerin boyutu derleme zamanında bilinmek zorunda değildir. Örneğin çalışma zamanında kullanıcının belirlediği sayıda elemana sahip olan bir ardışıl bellek bölgesi aşagıdaki programda oldugu gibi tahsis edilebilir.

using System;

class Gosterici
{

   unsafe static void Main()

   {
      Console.Write("Dizi boyutu gir: ");
      uint boyut=0;

      try
      {
         boyut = Convert.ToUInt32(Console.ReadLine());
      }
      catch(FormatException e)
      {
         Console.WriteLine(e.Message);
      }

      int * dizi = stackalloc int[(int)10];

      for(int i=0; i<(int)boyut; ++i)
      {
         *(dizi+i) = i;
         Console.WriteLine(dizi[i]);
      }
   }
}

Bu program ile çalışma zamanında 7 elemanlı stack tabanlı bir dizinin oluşturulduğunu aşağıdaki ekran görüntüsünden görebilirsiniz.

 

Yapı(Struct) Türünden Göstericiler

int,char,double gibi veri türleri aslında birer yapıdır. Bu konun başında bütün değer tipleri ile gösterici tanımlayabileceğimizi söylemiştik. C# temel veri türlerinin yanısıra kendi bildirdiğimiz yapılar da değer türündendir. O halde bir yapı göstericisi tanımlayabilmemiz doğal bir durumdur. Yapı göstericilerinin tanımlanması temel veri türlerinden gösterici tanımlama ile aynıdır. Ordaki kuralların tamamı yapılar içinde geçerlidir. Fakat yapı göstericisi tanımlamanın bir şartı vardır, oda yapının üye elemanlarının tamamının değer tipi olma zorunluluğudur. Örneğin aşağıdaki yapı göstericisi tanımlaması geçersizdir.

using System;

struct Yapi
{
   int x;
   char c;
   string s;

   public Yapi(int x,char c, string str)
   {
      this.x = x;
      this.c = c;
      this.s = str;
   }
}

class StackAlloc
{
   unsafe static void Main()
   {
      Yapi yapi = new Yapi(2,'a',"Deneme");

      Yapi* pYapi = &yapi;
   }
}

Yukarıda 'Yapi' türünden göstericinin tanımlanamamasının sebebi yapının yönetilen türden(managed type) bir üye elemanının bulunmasıdır. Bu üye elemanı da doğal olaral string türüdür. Yapı bildiriminden string türünü çıkarıp aşağıdaki gibi ilgili değişiklikleri yaptığımızda 'Yapi' türünden göstericileri tanımlayabiliriz.

using System;

struct Yapi
{
   int x;
   char c;

   public Yapi(int x,char c)
   {
      this.x = x;
      this.c = c;
   }
}

class StackAlloc
{
   unsafe static void Main()
   {
      Yapi yapi = new Yapi(2,'a');

      Yapi* pYapi = &yapi;
   }
}

Yapı göstericileri üzerinden yapı göstericisinin adresine ilişkin nesnelerin elemanlarına özel bir operatör olan -> operatörü ile erişebiliriz. Örneğin yukarıdaki programın Main() metodunu asağıdaki gibi değiştirdiğinizde ekrana yapı nesnesinin x ve c elemanları yazdırılacaktır. Tabi Yapi'nın üye alamanlarını public olarak değiştirmeniz gerekecektir. Çünkü ok operatörü ilede olsa ancak public olan elemanlara ulaşabiliriz.

unsafe static void Main()
{
   Yapi yapi = new Yapi(2,'a');

   Yapi* pYapi = &yapi;

   Console.WriteLine("yapi.x= " + pYapi->x);
   Console.WriteLine("yapi.c= " + pYapi->c);
}

Not: ' -> ' operatörüne ok operatörü de denilmektedir.

Yapının public olan elemanlarına ok operatörü yerine yapı nesnesinin içeriğini * operatörü ile elde edip nokta operatörü ile de ulaşabiliriz. Buna göre yukarıdaki Main() metodu ile asagidaki Main() metodu eşdeğerdir.

unsafe static void Main()
{
   Yapi yapi = new Yapi(2,'a');

   Yapi* pYapi = &yapi;

   Console.WriteLine("yapi.x= " + (*pYapi).x);
   Console.WriteLine("yapi.c= " + (*pYapi).c);

}

Her iki Main() metodunun ürettiği çıktı aynıdır.

Göstericilerin en çok kullanıldığı diğer bir uygulama alanı da karakter işlemleridir. Bir yazıyı karekter dizisi olarak temsil edip yazılar ile ilgili işlemler yapılabilir. C#' taki string türünün altında geçekleşen olaylarda zaten bundan ibarettir. Karakter dizileri ile ilgili en önemli nokta bir yazıyı char türden bir göstericiye atayabilmemizdir. Karekter dizileri olan stringlerdeki her bir karakter bellekte ardışıl bulunmaktadır. Dolayısıyla yazıdaki ilk karakterin adresini bildiğimizde yazıdaki bütün karakterlere erişebiliriz. C#'taki string türü yönetilen tip(managed type) oldugu için char türden bir göstericiye bir yazının ilk karekterinin adresini atamak için fixed anahtar sözcüğünü kullanmalıyız. Aşağıdaki programda bir yazının char türden göstericiye nasıl atandığını ve bu gösterici ile yazıdaki her karaktere ne şekilde erişildiğini görüyorsunuz.

using System;

   class KarakterDizisi
   {
      unsafe static void Main()
      {
         fixed(char* ptr = "Sefer Algan")
         {
            for(int i=0; ptr[i] != '\0'; ++i)
               Console.Write(ptr[i]);
         }
      }
}

Buradaki en önemli nokta ptr[i]' nin '\0' karakteri ile karşılaştırıldığı yerdir. Göstericiler ile bellekte char türünün büyüklügü kadar ilerlerken yazının nerede sonlandıgını bilemeyiz. Bunun için

char* ptr = "Sefer Algan"

deyimi ile belleğe yerlestirilen 'n' karakterinden sonra null değerini ifade eden '\0' karekter yerleştirilir. Bu karektere rastlanıldığı zaman yazının sonuna gelmiş bulunuyoruz.

Not: Göstericilerle ilgili yayınlanan bu makale dizisi yazmış olduğum ve yakında Pusula yayıncılıktan çıkacak olan C# kitabından alınmıştır.


 

Kaynak : csharpnedir.com


 

C Sharp (C#) İçin Eklenmiş Tüm Makaleler
C Sharp (C#) İçin En Son Eklenen 5 Makale
 Makale Adı  :  C#'da klasör işlemleri
 Yayın Tarihi :  11.04.2009 08:47:40 Ekleyen :  özen karaçakır Makaleyi Oku  
 Makale Adı  :  C# ile diziyi tersine çevirme
 Yayın Tarihi :  27.11.2008 08:57:42 Ekleyen :  özen karaçakır Makaleyi Oku  
 Makale Adı  :  Çok Kullanılan Kontroller-1
 Yayın Tarihi :  05.11.2007 10:39:39 Ekleyen :  Kazım Aydın Makaleyi Oku  
 Makale Adı  :  C# ile Kronometre Uygulaması
 Yayın Tarihi :  09.10.2007 11:23:04 Ekleyen :  Zeliha Görmez Makaleyi Oku  
 Makale Adı  :  Overloaded Metotların Gücü
 Yayın Tarihi :  12.08.2007 16:05:03 Ekleyen :  Kazım Aydın Makaleyi Oku  

C/C++
  C ögreniyorum -8-
  C ögreniyorum -7-
  C ögreniyorum -6-
  C ögreniyorum -5-
  C ögreniyorum -4-
  C ögreniyorum -3-
  C ögreniyorum -2-
  C ögreniyorum -1-
  İlk C++ Programımız

JAVA
  Java ile Histogram Eşitleme
  JAVA İçinde Thread Kullanımı
  Serialization İşlemleri 2
  Serialization İşlemleri
  İlk Bakışta JSP
  Servletler ile Web Sayfaları
  Java ile Grafik Çizim
  Java, MySql Bağlantısı
  JBuilder ile Database

PHP
  php dersleri -1-
  php/access ile silme işlemi
  php ile access'e bağlanma - 2
  php ile access'e bağlanma - 1
  Döngü ve Diziler - BÖLÜM 5
  Döngü ve Diziler - BÖLÜM 4
  Döngü ve Diziler - BÖLÜM 3
  Döngü ve Diziler - BÖLÜM 2
  Döngü ve Diziler - BÖLÜM 1

İSTATİSTİKLER
  Toplam ziyaret :  24742
  Toplam üye sayısı :  1081
  Asp makale sayısı :  20
  Asp.Net makale sayısı :  54
  Php makale sayısı :  26
  C/C++ makale sayısı :  51
  C# makale sayısı :  29
  Java makale sayısı :  33
  Toplam makale saıysı :  213
Copyright asundinay® 2007 Her Hakki Saklidir
Tasarim & Programlama asundinay®