Kodbank İndir

! CODEBANK 2012 !

İNDİRMEK&DETAYLI BİLGİ ALMAK İÇİN BURAYI TIKLAYINIZ.

ÖNEMLİ AÇIKLAMA: MUTLAKA OKUYUNUZ!

Gönderen Konu: Pointer ve Referans üzerine bir iki satır  (Okunma sayısı 2745 defa)

0 Üye ve 1 Ziyaretçi konuyu incelemekte.

Çevrimdışı Opt2000

  • Global Moderatör
  • *****
  • İleti: 263
  • Rep: +9/-1
  • Cinsiyet: Bay
Pointer ve Referans üzerine bir iki satır
« : 07 Ekim 2005 21:59:35 »
Daha önce bir öğrenci için hazırladığım Pointer ve Referans yazısını ekleyim dedim. Delphi'de yeni olan arkadaşlar varsa işlerine yarayacaktır.

Pointer

Matbaaya gidecek bir resim dosyanız var. 300 DPI, 50 x 70 (Diğer bir değişle yaklaşık 47 MB). Bu resim dosyasını belleğe aldınız. Şimdi bu resme bir efekt uygulayacaksınız. Efekt fonksiyonunuz da Kontrast olsun. Kontrast fonksiyonuna parametre olarak resmi göndereceksiniz, o da size sonucu gönderecek. Şimdi sen bütün resmi göndermeye kalksanız ne olur: Bellekte 47 MB transfer yapmış olursunuz. Bu da programı çok yavaşlatır. Pointer burada devreye giriyor. Siz sadece resmi bellekte nereye aldığını gönderiyorsunuz. Bu da sadece sizeof(integer) kadar veri gidecek demektedir. Yani 32 bit sistemlerde 4 byte! Aradaki fark inanılmaz değil mi?

Pointerın güzelliği burada bitmiyor. Gelişmiş dillerde, bellekten istediğin kadar yer açabilirsiniz. Ama bellekten yer açarken, compiler tarafından bunun ne tür bir data tipinde kullanacağın bilinemeyeceği için size sadece adres gönderebilir. Bu yüzden Visual Basic gibi dillerde bellek yönetimi otomatiktir. Bu da iyi programcıların elini kolunu bağlar.

Gelişmiş programlama dillerinde bellekten yer açmak istediğinizde, sadece blok halinde bir yer açılır ve bu yerin başlangıç adresi size geri gönderilir. İyi bir programcı olarak siz de bu başlangıç noktasından, bütün bellek alanına dilediğin gibi ulaşabilirsiniz.

Olayı biraz Delphi ile birleştirelim. Örneğin öğrenci bilgileri tutacaksın ve bu bilgileri bir record içinde tutmak istiyorsunuz:

Kod: Delphi
  1. Type
  2.        TStudentInfo=record
  3.        Name:string;          
  4.        Surname:string;        
  5.        Birthdate:TDateTime;  
  6.        ClassID:integer;      
  7.        Adres:PChar;          
  8. End;  
                       

Buraya kadar her şey çok basit ve normal. Şimdi bellekten bu data türü için yer isteyeceğimizde problem çıkacaktır. Çünkü bu data yapısı adres göstermez, datanın kendisini tutar. Bizim ayrıca bunun adres gösteren türünü tanımlamamız gerekir.

Kod: Delphi
  1. Type PStudentInfo=^TStudentInfo;

PStudentInfo, aslında sadece 4 byte yer tutan, ama bellekte, TStudentInfo tipinde bir bellek alanını gösteren pointerdır. Bu data tipini kullanarak bellekte sizeof(TStudentInfo) kadar yer açabilirsiniz.

Kod: Delphi
  1. Var                            
  2.        Tmp:PStudentInfo;      
  3. Begin                          
  4.        New(Tmp);                
  5.        Tmp.Name:=’Bahadır’;  
  6.        …                        
  7. End;
                         

…gibi.

Tmp değişkenini herhangi bir fonksiyona parametre olarak gönderebiliriz. Fonksiyon içinde bu değişkenin altındaki bilgiler değiştirilirse, ana fonksiyonda da değişir, çünkü her ikisi de aynı bellek alanını kullanmaktadır.

Kod: Delphi
  1. Procedure Main;
  2. Procedure Display(SInfo:PStudentInfo);
  3. Procedure Main;
  4. Var
  5.       Tmp:PStudentInfo;
  6. Begin
  7.       New(tmp);
  8.       tmp.Name:=’Bahadır’;
  9.       tmp.Surname:=’Alkaç’;
  10.  
  11.       Display(tmp);
  12.       ShowMessage(SInfo.Name + ‘ ‘ + SInfo.Surname);
  13.       //Mesaj kutusunda Ali Veli yazacak.
  14. End;
  15.  
  16. Procedure Display(SInfo:PStudentInfo);
  17. Begin
  18.       ShowMessage(SInfo.Name + ‘ ‘ + SInfo.Surname);
  19.       //Mesaj kutusunda Bahadır Alkaç yazacak.
  20.       SInfo.Name:=’Ali’;
  21.       SInfo.Surname:=’Veli’;
  22. End;

TStudentInfo yapısı içinde PChar tipinde Adres bilgisi de var. Adres bilgisi de Pointer tipinde bir değişken. Aslında bizim New(tmp) komutumuz yaklaşık şöyle olması

Kod: Delphi
  1. New(tmp);
  2. GetMem(tmp.Adres,255); //Adres için 255 byte yer açtık

New ile Getmem arasındaki basit farka gelince! New, TStudentInfo tipi kadar yer açar ve data tipinin içini, senin recorduna göre düzenler. Oysa GetMem de aynı zamanda ne kadar yer isteyeceğini de belirleyebilirsin.

Bu elemanlarla işin bittiğinde bunları bellekten silmek gerekir. Aksi takdirde Memory Leak oluşur. New ile açılan yer, Dispose ile silinir. GetMem ile açılan yer ise FreeMem ile silinir.

Pointer Aritmetiği
Pointerların aslında bir sayı olduğunu (integer) söyledik ama pointerların özel bir aritmetiği vardır. Hemen örnekle anlatayım:

Kod: Delphi
  1. Var
  2.       MyPointer:Pointer;
  3.       MyInteger:integer;      //FFAABB00 adresinde yaratıldığını kabul edelim
  4. Begin
  5.       MyInteger:=20;    
  6.       MyPointer:=@MyInteger;  //MyPointerın gösterdiği adres FFAABB00 oldu.
  7.          MyInteger:=MyInteger + 1;    //MyInteger=20 + 1 işlemini yaptık. MyInteger’in adresi hala FFAABB00
  8.       MyPointer:=MyPointer + 1;    //MyPointer’ın gösterdiği adres FFAABB04 oldu!
  9. End;

Peki niye 4 birim arttı: sizeof(integer)=4 (32 bit sistemlerde) olduğu için. Yani pointer aritmetiğini hesaplarken bütün işlemlerin sizeof(Pointer türü) ile yapıldığını düşünmek ya da sürekli type casting yaparak işlem yaomak gerekiyor. Pointerlar üzerindeki hakimiyetiniz artmadıkça, pointer aritmetiği gereken işlemlerden sakın, çünkü en zor tespit edilen hatalar, pointerlardan kaynaklı hatalardır.

Referance
Şimdi, PStudentInfo’yu bellekten silmeye yarayacak bir fonksiyon yazalım:

Kod: Delphi
  1. Procedure Main;
  2. Begin
  3.       DeleteStudentInfo(tmp);
  4.       Dispose(tmp);
  5.       Tmp:=nil;
  6. End;
  7.  
  8. Procedure DeleteStudentInfo(SInfo:PStudentInfo);
  9. Begin
  10.       FreeMem(SInfo.Adres);
  11.       FreeMem(); //Daha başka neler tanımlandıysa artık.
  12. End;

Asıl sorun, ana fonksiyonda basit bir yapıyı silmek için 3 satır kod yazmamız.

Kod: Delphi
  1. DeleteStudentInfo(tmp);
  2. Dispose(tmp)
  3. Tmp:=nil;

Ben bu gibi durumlarda, DeleteStudentInfo’nun bütün işi bitirmesini isterim. Referance burada devreye giriyor işte. Pointer’ı, bir fonksiyona parametre olarak gönderdiğinde, pointer’in tuttuğu adres, fonksiyondaki değişkene atanır ve böylece aynı bellek alanını kullanırsın. Ama pointer’ın bellekteki adresi ile fonksiyonun parametre değerinin bellekteki adresi birbirinden farklıdır. (Gösterdikleri yer aynıdır).

Tablodaki bütün adres değerlerini kafadan attım, sadece örnek olsun diye yazıyorum.
Değişken ---------------------   Kendi Adresi -------  Bellekte İşaret Ettiği Adres
Tmp (Main’de tanımlandı) ------ FFAA3344 ---------FFAABBCC
SInfo(Parametre) ---------------- FF556677 ---------- FFAABBCC

Sonuçta pointer aslında bir sayıdır ve kendisi de bellekte yer tutar. İşin bütün esprisi budur.
Oysa bir SInfo parametresini Referance olarak tanımlasaydık ne olacaktı:

Değişken ---------------------   Kendi Adresi -------  Bellekte İşaret Ettiği Adres
Tmp (Main’de tanımlandı) ---- FFAA3344 ------------ FFAABBCC
SInfo(Parametre)  --------------  FFAA3344 ------------ FFAABBCC

Yani birebir aynısı. Bu durumda Delete fonksiyonunda şunu da yazabilirdim:

Kod: Delphi
  1. Dispose(SInfo);
  2. SInfo:=nil;
Böylece Main fonksiyonunda sadece DeleteStudentInfo(tmp) yazmam yeterli olacaktı. İşte Referance ile pointer farkı bu kadar basit.

Delphide referance tanımlamak da çok basit:

TList
TList, adı gibi bir listedir. Ama bu liste Linked List’tir.

TList, içeriğinin ne olduğunu bilmeden data tutabilen bir listedir. Bu cümleyi yazmamın tek sebebi, TListin ile Array arasındaki en büyük farkı belirtmek. Bir TList içine her tür pointer değişkeni ekleyebilirsin. Ayrıca Array, bellekte ardışık yer açmaya çalışır. Oysa TList’de tutulan adresler için bellekte ardışık yer açılmasına gerek yoktur. Ama nasıl?

TList’in Linked List olduğunu söylemiştim. Linked List’i anlattıktan sonra TList’i anlamanız çok kolay olacak ve açıkçası hayran kalacaksınız.

Linked List’in mantığı çok basittir. TStudentInfo yapısını tekrar tanımlayalım, ama bu sefer, önce Pointer’ını, sonra yapıyı tanımlayalım.

Kod: Delphi
  1. type
  2. PStudentInfo = ^TStudentInfo; //Önce pointerını tanımladık
  3. TStudentInfo=record //Şimdi recordu tanımlıyoruz
  4.       Name:string;
  5.     Surname:string;
  6.       ...
  7. NextItem:PStudentInfo;
  8. End;
NextItem değişkenine dikkat ettiniz mi? PStudentInfo tipinde. Yani kendisi gibi bir elemana işaret ediyor. Şimdi nasıl kullanılacağına bakalım.

Kod: Delphi
  1. Var
  2.       FirstItem:PStudentInfo;
  3.       Tmp:PStudentInfo;
  4. Begin
  5.      New(tmp);
  6.       Tmp.Name:=’Bahadır’;
  7.       Tmp.surname:=’Alkaç’;
  8.       …
  9.       tmp.NextItem:=nil;
  10.       FirstItem:=tmp; //Bu bir daha değiştirilmeyecek. İlk elemanı her zaman bilmemiz gerekiyor.
  11.       New(FirstItem.NextItem);
  12.       FirstItem.NextItem.Name:=’Ali’;
  13.       FirstItem.NextItem.Surname:=’Veli’;
  14.       …
  15.       FirstItem.NextItem.NextItem:=nil;
  16. End;
Ben ikinci elemana FirstItem üzerinden eriştiğim için kodlaması zor gibi görünebilir, ama aslında böyle kullanılmaz. Basit bir fonksiyon hazırlarsın ve fonksiyonda döngü ile n. Elemanın adresini geri döndürürsün. İş biter, kodlaması da kolay olur.

Bunun avantajı nedir? Bellekte peş peşe yer açmak (Array’de olduğu gibi) daha fazla vakit alır, çünkü datanız büyükse, boş yer arayacak, kimi zaman OS (Operating System) bellekte bin türlü yer değiştirme yapacak falan filan. Oysa Linked List de her seferinde sadece bir elemanlık yer açmış olacaksınız. Bu da sana inanılmaz bir hız kazandıracak. (Indexli erişim gereken durumlar bunun dışındadır. Resimler gibi. Bir de genelde optimizasyondan dolayı 1 tane değil, 2n + 1 tane yer açılır.)

TList ise, linked lit metodunu senin yerine kullanır, sıralar, indexli erişim sağlar, optimize eder. Yani fazla güçlüdür. Kullanımı çok basittir.
Kod: Delphi
  1. Tmp:TList;
  2. Tmp:=TList.create;
  3. Tmp.add (SInfo1); //SInfo1 Pointer olmak zorunda
  4. Tmp.add (SInfo2); //SInfo2 Pointer olmak zorunda
  5. Sınfo1:=Tmp.Items[0]; //Elemanlar 0’dan başlar. Count-1’e kadar gider.
  6. Tmp.Delete(0); //İkinci elemanın indeksi 1’di, artık 0 oldu.
Dikkat ederseniz komutlar inanılmaz derecede basit!

TList’in altyapısı kitaplarda ve Delphi Help’te anlatılmadığı için yazdım. Kullanımı ile ilgili bilgileri en iyi Delphi Help’ten öğrenebilirsin.

TList’in altındaki Linked List mantığını bildikten sonra, bunu istersen C, C++ gibi diğer programlama dillerinde de kullanabilirsiniz.


Kod: Delphi
  1. Procedure DeleteStudentInfo (var SInfo:PStudentInfo);


Değişkenin başına var yazdığında değişken referans olarak gönderilir.


DÜZELTME:
Bu yazıyı yazdıktan uzun bir süre sonra TList'in kodlarına baktım ve orada Borland'ın aslında TList'i dediği gibi Linked List olarak değil, gerçekten de List olarak tanımladığını gördüm. Linked List ile ilgili yazılanlar doğru, ama TList gerçek bir Linked List değil.
« Son Düzenleme: 24 Haziran 2009 01:03:33 by Kocaturk »

Çevrimdışı maydin60

  • Kıdemli Üye
  • *****
  • İleti: 76
  • Rep: +0/-0
  • Cinsiyet: Bay
Ynt: Pointer ve Referans üzerine bir iki satır
« Yanıtla #1 : 10 Temmuz 2010 10:45:34 »
Gerçekten Kullanılması gereke bir yapı...
Programlarda ben bu yapıyı kullanıyorum..
Özellikle Datamodul de Tablo alan değerlerini ,  bu list yapısının içinde kullanarak
yüzlerce işleme sokuyorumm...
İşlemler müthiş derece hızlı ve seri oluyor.

Programcıların gerçekten ögrenmesi ve kullanması gerekn bir yapı


Güzel bir anlatım olmuş....
Tşk.
« Son Düzenleme: 10 Temmuz 2010 10:47:21 by maydin60 »
Nokta Kadar Menfaat için Virgül Kadar Eğilme...