LinkedIn FriendFeed Twitter

Bağlı Listeler

by Ordinaryus 8. February 2009 19:40

Bağlantılı listeler bellekte ayrıkolarak saklanan verilerin birbirleri ile sanal olarak bağlı tutulması yoluylaverilerin ardışıl olarak saklanabilmesine olanak sağlayan veri yapısıdır. buveri yapısının oluşmasında en az 2 kısım altında oluştuğunu söyleyebiliriz.Bunlardan ilki saklanması istenilen veri ve diğer kısım ise veri ekleme,çıkarma işlemlerinin yapılmasına olanak sağlayan işaretçilerdir. Yani bağlılisteler temelde 2 kısımdan oluşmaktadır. Bu dokumanda bağlı listeler hakkındabilgi verilecektir.

 

·        Bağlı liste kavramı

·        Listelerin bellekte tutulma biçimi

·        Ayrık alanlarda liste tutulması

·        Tek yönlü bağlantılı listeler

·        Çift yönlü bağlantılı listeler

 

Bağlı Liste Kavramı

Bağlantılılisteler verilerin bellek üzerinde sanal bağlar ile birleştirilerek ardışılbiçimde saklanması ile ortaya çıkmıştır. Bağlı listelerde dinamik bellekkullanılabilir olması onların belleğin farklı parçalarında oluşmasına nedenolmaktadır. Bu problemin çözümü için bağlı liste oluşturulacak verilerin yapısındakendinden sonra gelecek olan verinin adresinin saklı tutulacağı bir işaretçikullanılmaktadır. Bu işaretçiler sayesinde liste üzerinde ilerlemek, kayıteklemek veya silmek mümkün olmuştur. Bağlı listelerin tümünde bir ilk işaretçioluşturulmaktadır. Bunun gerekliliği bağlı liste üzerinde işlem yaparken bu ilkdeğerden başlayarak ilerlememiz gerektiğindendir. Ara değerlere doğrudanulaşmak mümkün değildir. N. elemana ulaşmak için bir döngü içerisinde ilkdeğerden başlanarak ilerlenir ve istenilen veriye ulaşılır. Ekleme işleminde debu yöntem kullanılabilmektedir. Fakat bu işi sadece başlangıç pointerıkullanarak yapmak çok zahmetlidir. Listedeki tüm elemanları takip edip sonuncaelemana ulaşıp sonrasında ekleme yapmak gerekir. Bunun yerine birde sonpointerı tanımlarsak bu engel ortadan kalkar ve doğrudan listenin sonuna verieklemek mümkün olur.

 

Bağlı Listelerin Bellekte Tutulması

Bağlı listelerdeverilerin saklanmasında dizi üzerinde saklama veya dinamik bellek kullanılarakprogramın çalışması sırasında bellek tahsis etmek mümkündür. Dizi üzerindesaklama yapılacağında dizinin boyutu derleme öncesinde belirtilmek zorundadırve programın işlevinin özelliklerine göre tahmini bir boyutta dizi oluşturmakmümkündür. fakat girilecek olan verilerin istenilen dizi boyutunu aşmasıproblem oluşturacaktır. Bu nedenle dinamik bellek kullanmak daha iyi biryoldur. Malloc fonksiyonu ile bellekten veri alanı tahsil edilir ve bu verialanları birbirleri ile işaretçiler yolu ile bağlanarak boyutu değişken bir verigrubu elde edilir. Bu yöntemde ihtiyaç kadar bellek ayrılması bellek kullanımıaçısından da oldukça avantajlı bir yoldur. Malloc fonksiyonları ile açılanbellek gözleri free() fonksiyonu ile boşaltılabileceğinden işe yaramayansilinen verilerde bellekte fazladan yer tutmamış olur. Bu nedenle bir bağlıliste üzerinde saklanmasında bu yöntem daha avantajlıdır.

 

Ayrık Alanlarda Bağlantılı Liste Uygulaması

Ayrık alanlardadinamik tek yönlü bir bağlantılı liste oluşturulmasında kullanılan belli başlıtemel fonksiyonlar vardır ve bu fonksiyonlar tüm uygulamalarda uyarlanılarakkullanılabilir. Bunlar ekleme, listeleme, arama ve silme fonksiyonlarıdır. Amabundan önce yazacağımız uygulamanın ihtiyaçlarına göre bir veri türütanımlamamız gerekmektedir. Bu veri türünün içerisinde saklamak istediğimizveriler bulunmakla beraber birde işaretçi türünden bir değişkentanımlanmaktadır. Ayrıca listenin başını ve sonunu işaret edecek 2 adetişaretçi tanımlanmalıdır. Bu işaretçiler liste boş iken NULL değerlerinigöstermektedir. Ve işlemler gerçekleştirildikçe değişmektedir.

Ekleme işlemindeyapılması gereken ilk şey listeye daha önce bir eleman eklenip eklenmediğinikontrol etmektir. Çünkü bu 2 durum arasında yapılacak işlemler açısındanfarklılıklar bulunmaktadır. eğer ilk kez bir eleman eklenecekse ilk ve sonpointerları NULL değerdedir ve yapılan işlemlerle ilk ve son pointerları buyeni eklenen elemanın adresini saklayacaktır. Daha sonra eklenecek elemanlarilk değişkenine dokunmayıp, son adlı işaretçiyi yeni eklenen elemanın adresinigösterecek şekilde değiştirecektir. Bu işlem sonunda listenin eleman sayısı 1artmış olacak ve son pointerı en son eklenen elemanı işaret edecektir.

Listelemeişleminde ilk elemanı saklayan pointer aracılığı ile başlanır ve sıra ile tümelemanlara ulaşılır ve onların saklandığı değerlerin alınması ile devam eder.NULL değerine karşılık gelen son pointerına ulaşıldığında listenin sonunagelindiği anlaşılmış olur.

Arama işlemi velisteleme işlemi arasında bir fark yoktur. Gene aynı prosedüre göre işler veelemanların değerleri ile aranan değerler karşılaştırılır. Eğer aranan değerbulunursa arama işlemi sonlanır. Eğer NULL değere kadar devam ederse listede buarama kriterine uygun bir sonuç bulunamadığı anlaşılır.

Silme işlemindede arama işlemine ihtiyaç duyulmaktadır. Tek yönlü listede silme işlemi çiftyönlü listeye göre daha zahmetli olarak yapılmaktadır. Silme işlemi içinöncelikle silinmek istenen eleman aranmaktadır. Silinmesi istenen ve silinecekolan elemandan bir önceki elemanın adresleri bulunmakta ve silinmek istenenveri aradan çıkarılmaktadır. Bu işlem sırasındada silinecek olan düğümün tekdüğümü yoksa başka düğümler olup olmadığına göre farklı işlemler yürütülür.Aradaki elemanın çıkartılması sırasında kendinden önceki elemanın işaretçisiyani silinen elemanı gösteren değer, silinecek elemandan bir sonraki elemanıişaret edecek şekilde ayarlanır ve aradı elemanın bellekte kapladığı alan freefonksiyonu ile ortadan kaldırılır.

#include<stdio.h>

#include<stdlib.h>

#include<conio.h>

 

typedef struct klup{

        int numara;

        char isim[15];

        struct klup *arka;

        }OTOKON;

       

OTOKON *ilk=NULL, *son=NULL;

 

OTOKON *ara(int);

OTOKON *sil(int);

OTOKON *gir();

void yaz(OTOKON*);

int listele();

int ekle(OTOKON *p);

main()

{

      OTOKON *eklenecek,*p;

      int numara,sonuc;

      char secim;

      while(!0){

              printf("Ekleme, Listeleme, Arama, Silme, Cikis seciniz\n");

              secim=getch();

              switch(secim){

                     case 'E': eklenecek=gir();

                               if(eklenecek!=0)

                                   ekle(eklenecek);

                               else printf("Ekleme icin bellek yok\n");break;

                     case 'L': sonuc=listele();

                               if(sonuc==-1) printf("Liste Bos\n"); break;

                     case 'A': printf("Aranan: ");

                               scanf("%d",&numara);

                               p=ara(numara);

                               if(p==NULL)

                                     printf("Aranan bulunamadi\n");

                               else

                                     yaz(p); break;                           

                     case 'S': printf("Silinecek: ");

                               scanf("%d",&numara);

                               p=sil(numara);

                               if(p!=NULL)

                                     printf("silindi..\n");

                               else

                                     printf("Boyle bir veri yok\n"); break;

                     case 'C': printf("gene bekleriz"); exit(0); break;

                     default : printf("boyle bir secim yapilamaz\n");    

                     } 

                }

}

 

int ekle(OTOKON *p)

{

         if(ilk!=NULL){

                 son->arka=p;

                 son=p;

                 son->arka=NULL;     

                       }   

         else{

                 ilk=p;

                 son=ilk;

                 ilk->arka=NULL;     

              }

return 0;

}

 

int listele()

{

        OTOKON *p;

        p=ilk;

        if(p==NULL) {return -1;}

        while(p){

                yaz(p);

                p=p->arka;

                 }   

        return 0;

}

 

OTOKON *ara(int aranan)

{

       OTOKON *p;

       p=ilk;

       while(p){

              if(p->numara==aranan) return p;

              p=p->arka; 

                }      

       return NULL;        

}

 

OTOKON *sil(int silinecek)

{

       OTOKON *p,*bironceki;

       p=ilk;

       bironceki=NULL;

       while(p){

               if(silinecek==p->numara) break;

               bironceki=p;

               p=p->arka;

                }     

       if(p!=NULL){

               if(bironceki==NULL){

                  if(ilk==son){

                          ilk=NULL;

                          son=NULL;    

                               }  

                  else

                       ilk=ilk->arka;

                  }            

                      

       else{

            bironceki->arka=p->arka;

            if(bironceki->arka==NULL)

                  son=bironceki;

            }               

            free(p);

            return p;    

            }

            else

                return NULL;                  

}

 

void yaz(OTOKON *yazilacak)

{

     printf("numara:%d, isim:%s\n",yazilacak->numara, yazilacak->isim);    

}

 

OTOKON *gir()

{

       OTOKON *okunan;

       okunan=(klup*)malloc(sizeof(OTOKON));

       if(okunan==NULL)

                  return NULL;

       else{

       printf("Numara giriniz: ");

       scanf("%d",&(okunan->numara));

       printf("isim giriniz: ");

       scanf("%s",&(okunan->isim));

       return okunan;}    

}

 

Çift Yönlü Bağlantılı Liste Uygulaması

İki yönlüdüğümlerde tıpkı tek yönlülere benzer şekilde işlemekte ve oluşturuluyordiyebiliriz. Fakat çift yönlü listelerde öndeki ve arkadaki elemanlarlabağlantıyı sağlayan işaretçiler kullanılmıştır. Bu nedenle liste üzerinde çiftyönlü olarak hareket etmek mümkün olmuştur. Fakat iki yönlü listenin ekleme veçıkarma yapılırken öndeki ve arkadaki elemanlarla bağlantıyı sağlayacak olan 2adet işaretçisi olması aradan bir düğüm çıktığından ve eklendiğinde yenidenkurulması gereken 2 adet bağlantı olduğu anlamını taşımaktadır.

Bağlı listelerintemel işlemleri olan ekleme, çıkarma, listeleme ve arama işlemleri mantıkolarak aynı şekilde işlemektedir. 

 



Yukarıdakiörnekten de anlaşılacağı üzere liste üzerinde çift yönlü hareket etmekmümkündür. Burada listeye yeni düğüm ekleme işlemi de basitçe gösterilmeye çalışılmıştır.Dizinin görece ilk ve son elemanları belirlenmiş eklenen yöndeki gösterici yenielemanı gösterecek şekilde ayarlanmıştır böylece dizinin uçlarının belli olmasısağlanmaktadır. Ekleme işleminden önceki son elemanın NULL olan sonraki elemanıgösteren işaretçi değeri yeni son elemanı gösterecek şekilde değiştirilmiştir.Daha sonra son elemanın kendinden önceki elemanı gösterecek işaretçisinintanımlanmasıyla ekleme işlemi tamamlanacaktır.

Çift yönlü bağlılistelerde listeleme ve arama işlemlerinin tek yönlü bağlı listelerden farkıbulunmamaktadır. Bu kısımda daha çok ekleme ve silme fonksiyonlarındanbahsederek çift yönlü bağlı liste yapılandırılmasını anlamış olacağız.

 

int ekle(OTOKON *p)

{

         if(ilk2!=NULL){

                 son2->arka=p;

                 p->on=son2;

                 son2=p;

                 son2->arka=NULL;     

                       }   

         else{

                 ilk2=p;

                 son2=p;

                 ilk2->arka=NULL;

                 ilk2->on=NULL;     

              }

return 0;

}

  

Yukarıdakifonksiyonda görüleceği üzere çift yönlü bağlı listeye ekleme yaparken de ilkyapacağımız şey daha önceden herhangi bir düğüm yaratılmış mı diye kontroletmek olacaktır. Eğer bu eleman ilk düğümü oluşturacaksa ilk ve sonişaretçilerimizin bu elemanı göstermesini sağlar daha sonra bu elemanınardından gelen elemanları belirlemek için kullandığımız işaretçileri NULLolarak tanımlarız. Daha sonra var olan düğüm veya düğümler üstüne yeni birdüğüm ekleyeceğimizde ise öncelikler son ile tanımladığımız işaretçinin yenielemanı göstermesini sağlar. Daha sonra bu yeni elemanın önündeki değişkenitutan on işaretçisi ile son adlı elemanı eşleriz. Bu aşamadı bağlantıları yapmışbulunmakta daha sonraki ekleme işlemlerinin yapılabilmesi için son iletanımladığımız değişkenin yeni değerlerini vererek güncellememiz gerekmektedir.Bunun için son düğümün en son eklenen olduğunu belirtir ve sondan sonrakielemanı gösteren işaretçi NULL olarak belirlenir. Temelde tek yönlü bağlılistelere göre daha çok atama işlemi yapılması gerekse de mantık olarak basittemellere dayandığından anlaşılması kolay olmaktadır.

 

OTOKON *sil(int silinecek)

{

       OTOKON *p;

       p=ara(silinecek);

       if(p==NULL)

                  return NULL;

       if(ilk2==p){

              if(ilk2->arka!=NULL){

                      ilk2=p->arka;

                      ilk2->on=NULL;            

                                   }    

              else{

                      ilk2=NULL;

                      son2=NULL;                 

                   }

                   }           

       else{

              p->on->arka=p->arka;

              if(p==son2)

                         son2=son2->on;

              else

                         p->arka->on=p->on;

            }

       free(p);

       return p;   

}

 

            Silmeişlemi de tek yönlü bağlı listelerdeki gibi işlemektedir. Aradaki bağlantılarınfazlalığı yazılan kodun uzunluğunu arttırsa da temel atama işlemleri olduğundanmantıksal açıdan anlaşılmasında sorun oluşmamaktadır. Burada da başlarkenyaptığımız şeyi tekrarlayacak ve listede kaç düğüm olduğunun ve bizim silmekistediğimiz elemanın hangi düğümde olduğunun önemini vurgulayarak silinecekolan düğümün ilk düğüm olup olmadığını sorgulayacağız. Eğer silinecek olaneleman ilk düğümde yer alıyorsa bu listede kaç düğüm olduğu sorusu da devreyegirecektir. Eğer listede sadece bir düğüm varsa bunu silmek için ilk ve sondiye adlandırdığımız işaretçiler NULL yapmamız yeterli olacaktır. Şayet listedebirden çok düğüm varsa biz ilk elemanı silmek için ilk işaretçisi ilegösterilen elemanın kendinden sonraki elemanı göstermede kullandığı işaretçiyiilk değerine atamamız gerekmektedir. Daha sonra dizinin yeni ilk elemanının,öndeki elemanı gösteren işaretçisi,  NULL  yapılır. Daha önceden buişaretçi ilk elemanı işaret etmekteydi fakat şimdi ilk değer olarakatandığından NULL değer almalıdır. Bu kısımdan sonra baştaki koşula geridönebiliriz. Bu koşulda silinmek istenen eleman dizinin ilk elemanı değilseyapmamız gerekenlere bakalım. Öncelikle yapmamız gereken silinmesi istenenelemandan önceki eleman ve sonraki eleman arasındaki bağlantıyı sağlamaktır.Bunun için silinmek istenen değişkenin kendinden önceki elemanı göstermeyeyarayan işaretçisi kullanılarak bir önceki elemana ulaşılır. Bu elemanın isesilinmek istenen elemanı gösteren yani kendinden sonra gelen elemana yönlenmişbir işaretçisi bulunmaktadır. Bu değişkenin değeri arada elemanın çıkmasıylagüncellenmeli bu durumda o konumda yer alacak elemanı göstermelidir ki bueleman sileceğimiz elemandan bir sonraki elemandır yani bu elemana sileceğimizelemanın sonraki elemanı gösteren işaretçisini kullanarak ulaşabilmekteyiz. Buişlemi gerçekleştirdiğimde yapmış olduğumuz şey temelde silinen elemanıatlayarak önceki ve sonraki elemanı bağlamaktır. Fakat işimiz burada bitmemekteçünkü daha ters yönde oluşturduğumuz bağlar bulunmakta. Bu noktada önemli olandurum silmek istediğimiz elemanın son eleman olup olmadığıdır. Eğer sonelemansa yeni son elemanı kendinden önceki değere atamak gerekir. Bunuyaptığımızda artık silinmek istenen elemandan önceki eleman son elemanolmuştur. Diğer duruma dönülecek olursa yani düğümün özel bir konumu yoksaarada bir yerde yer alıyorsa silinmek istenen elemanın kendinden sonraki değerigösteren yardımıyla bir sonraki elemana geçilir ve bu elemanın önceki elemanıyani silinecek elemanı gösteren işaretçisi silinme işleminden sonra burayagelecek olan silinmek istenen elemandan önceki elemanı gösterecek şekildeatanır. Hangi durum gerçekleşirse gerçekleşsin unutulmaması gereken bir diğerönemli nokta ise silinen elemanın bellekte tuttuğu alan boşaltılmalıdır. Bubizi bellek israfından kurtarmaktadır.     

     Yukarıda anlatılanlardan sonra son olarakbirkaç şeye değinmekte fayda olduğunu düşünüyorum. Her ne kadar kodlar uzun vekarmaşık gözükse de bağlı listeyi çizerek veya göz önünde canlandırarak aradıbağları kurmak veya yeniden yapılandırmak oldukça kolay bir işlemdir. Silme, eklemegibi işlemlerin kodlarını bu bakımdan hafızada tutmaya çalışmak mantıksızdır.Yapının nasıl oluşturulduğu analiz edebilmek ve bunu koda dökebilmek bueğitimin temel amacıdır. Çünkü ileride karşılaşacağımız bir problem buradakitemel veri yapıları ile kullanışlı olarak çözülemeyebilir. Bu durumda çözümeuygun bir veri yapısını kendimiz oluşturmamız gerekmektedir. Zaten temelde pekçok problemin çözülememesi veya yarım bırakılması uygun yapını seçilememesindenkaynaklanmaktadır. Bu nedenle veri yapısı oluşturmanın mantığı anlaşıldığındaproblemlere bakış açısı oluşturmak daha kolay olacaktır.   

 

Kaynaklar

·        Data structures and program design in c, RobertL. Kruse

·        Data structures and algorithms, Adam Drozdek

·        Veri yapıları ve algoritmalar, Rıfat Çölkesen

Tags:

Yığın, Kuyruk

by Ordinaryus 8. February 2009 19:31

YIĞIN (STACK) YAPISI

Bilgisayarbiliminde, ister donanım olsun ister yazılım veri modelleri oldukça sıkkullanılmaktadır. Mikroişlemcinin çalışmasını incelediğimizde bu yapılarınnasıl işlediğini görebiliriz. Mikroişlemci içerisinde yığın işaretçisi denilenbir pointer bulunmaktadır. Bu pointer mikroişlemciye bir kesme(interrupt)geldiğinde o an bellekte işlenen veriyi belleğin bir başka adresine işaretederek saklamakta veya program içerisinde alt programlara veya fonksiyonlaradağıldığında parametreler ve geri dönüş değerleri yığın modelinde saklanır.

Yığın modeli çokklasik bir örnek vermek gerekirse üst üste dizili tabaklarabenzetilebilinmektedir. Bu tabaklardan en alttakine ulaşmak istediğimizi farzedelim. Bu durumda üstteki tüm tabaklardan oluşan yığını kaldırmak suretiyleistenilen tabağa erişimi gerçekleştirmiş olmaktayız. Yığın yapısında kuyrukyapısından farklı olarak yapıya son giren eleman ilk olarak çıkmaktadır. Yığınyapısına ayriyeten LIFO (last in first out) denmektedir ve çalışma şekliniaçıklayıcı bir tanımdır.

      

Yukarıdaki şekilde daha açık ifadeedildiği üzere yığın yapısında belleğe veri yazmak ve bellekten veri almak içinkullanılan fonksiyonlar yardımı ile belleğe erişilmektedir. Burada p ilegösterdiğimiz değişken bir işaretçidir. Bu işaretçi stack yapısına uygun veri eklenebilecekilk bellek gözünü işaret etmektedir. Görüldüğü üzere yığın yapısında belleğeson girilen değer olan 7 ilk çıkmaktadır. Burada önemli bir noktada pişaretçisinin gösterdiği bellek gözünün alabileceği 2 önemli konumdur.Bunlardan ilki p işaretçisi ilk bellek gözünü gösteriyor ise yığın boşturdemektir ve bu göze veri yazıp işaretçi bir sonraki bellek gözüneyönlendirilir. Diğer bir durum ise işaretçi son bellek gözünü gösteriyor ise buyığın dolu demektir ve yığına yeni veri eklemek mümkün değildir. Yığın yapısıüzerinde işlem yaparken bu koşulların kontrolünü sağlayacak koşullarkonmalıdır. Yığın yapısında temel olarak 3 işlem vardır. Ayrıca 2 tanedekontrol işlemi yazmak mümkündür.

·        Koy(push)

·        Al(pop)

·        Sıfırla(reset)

·        Dolumu(isFull)

·        Boşmu(isEmpty)

Yığın ve kuyrukişlemleri için bu fonksiyonlar ayrı ayrı yazılabileceği tümünü kapsayan tek birfonksiyon yazmakta mümkündür. yığın yapısı bir dizi üzerinde kurulabileceğigibi bağlantılı liste modeli kullanılarak da gerçeklenebilir.      

Yığın yapısındaveriler bağlantılı listeler ve diziler üzerinde tutulabilir. Dizilerde yığınyapısı kullanmak kolaydır çünkü bellekte veriler ardışıl olarak saklanmaktadır.Bağlantılı liste ile yığın yapısı oluştururken bellekte ayrı ayrı bulunmaktaolan verilerin ardı ardına sıralanması ve bu veriler arası bağlantınınpointerlar yardımıyla yapılmasına dayanır. Aşağıda yağın yapısı içinoluşturulmuş bir struct ve yığın fonksiyonları bulunmaktadır.

 

#include<stdio.h>

 

struct yigin{

       int p;

       int yiginveri[10];

            }Y;

           

int koy(int veri){

            if(Y.p>=10){

                  printf("Bellek dolu");     

                        }

            else

                  Y.yiginveri[Y.p]=veri;

                  Y.p++;

               }

 

                

int al()

{

            if(Y.p<=0){

               puts("Yigin Bos");

                      }

            else

                return Y.yiginveri[--Y.p];  

}

 

void temizle()

{

     Y.p=0;

}

 

void yaz()

{

     for(int i=0;i<Y.p;i++){

     printf("%d ",Y.yiginveri[i]);

     printf("\n");                }

}

main()

{

      int tercih,ekle;

      for(;;){

              printf("Eklemek icin '1', cikarmak icin '2', bosaltmak icin '3' giriniz\n ");

              scanf("%d",&tercih);

              switch(tercih){

                     case 1: printf("Eklenecek sayiyi giriniz\n");

                             scanf("%d",&ekle);

                             koy(ekle);

                             yaz(); break;

                     case 2: al();

                             yaz(); break;

                     case 3: temizle();

                             yaz(); break;

                     default: printf("Duzgun bir sayi giriniz\n");    

                             }

             }

     

while(2);     

}

 

KUYRUK (QUEUES) YAPISI

Kuyruk da en azyığın kadar önemli bir veri yapısı türüdür. Çalışma şekli olarak temelfarklılıkla bulunmaktadır. Bunlardan en önemlisi yığındaki son giren ilk çıkarmantığının aksine kuyruk yapısında ilk giren ilk çıkar (first in first out)FIFO mantığı işlemektedir. Kuyruk denmesinin esas nedeni budur gerçek dünyadakiyemek sırası kuyruğu gibi işlemektedir (Öğrenci kartına para yüklüyorsanız oayrıJ). Kuyruk modeli program tasarımında bir çok yerde kullanılmaktadır. İki birimarasındaki veri aktarımı, ara bellek, tampon bellek ve öncelik kuyruğuoluşturmada bu yöntem kullanılır. Kuyruk tasarımı yapılırken çeşitli yöntemlerkullanılabilir. İçlerinden en yalını bir dizi ve bir indis değişkenikullanılarak yapılanıdır. Bu yapıda stack yapısında olduğu gibi veriler birdizide tutulur ve bir pointer ile kontrol edilir. Fakat bu kez pointer songiren veriyi işaret eder. Kuyruktan eleman çıkarma işlemi ise her zaman dizininbaşlangıç adresinden yani 0 indisli bloktan başlanılarak yapılır. Fakat buyöntemin en kötü yanlarından birisi elemanlardan biri çıkarıldığında onunarkasında değişkenlerin tamamı bir blok ileri kaydırılmaktadır ve bu fazlacaişlem edebilmektedir. Kuyruk tasarımında daha pratik başka çözümlerdemümkündür. Kuyruk tasarımı için genel olarak 3 değişik çözüm şekliönerilir. 

·        Dizi üzerinde kaydırmalı (bir indis değişkenli)

·        Dizi üzerinde çevrimsel (iki indis değişkenli)

·        Bağlı liste ile

 

  

Dizi üzerinde kaydırmalı

Dizi üzerindekaydırmalı kuyruk yapısı belki en kolay yapı olabilir. fakat bu yöntemin eksikyönleri kuyruktaki eleman sayısı arttıkça daha çok problem oluşturmaktadır. Amaaz sayıda elemanı olan kuyruk yapısında kullanılması çok sorun olmamaktadır. Buyapıda kuyruk içerisindeki ilk eleman kuyruktan çıkmaktadır bu eleman aynızamanda kuyruğa giren ilk elemandır. Bu elemanın yani 0 indisli elemanınkuyruktan çıkarılması sonucu 1 indisli eleman sıfıra, 2 indisli eleman 1e gelirve bu böyle devam eder bu işlem nedeniyle dizi üzerindeki kaydırmalı kuyrukyapısı çok elemanlı durumlarda tercih edilmemektedir.

int cıkart()

{

    if(son<0){

           puts("Kuyruk Bos");  

              }   

    veri=K[0];

    for(int k=0;k<=son;k++)

            K[k-1]=K[k];

    son--;

    return veri;

}

 

int ekle(int veri)

{

    son++;

    if(son>N){

           puts("Kuyruk Dolu");

           son--;  

              }   

    K[son]=veri;

}

 

 

 

Dizi üzerinde çevrimsel

Bu yöntemde diziüzerindeki eleman kaydırma problemine bir çözüm üretilmiştir. Burada verilersanki dairesel bir bellek üstündeymişçesine kaydedilmektedir. Bellekteki sonadrese gelindiğinde bir sonraki adres ilk adres olmaktadır yani başadönmektedir. Bu yöntemi kullanırken dizinin sonunu nasıl saklıyor isekayriyeten birde başını saklayan bir pointer kullanmaktayız. İlk pointerı bellektenokuma ve alma işlemlerini yaparken, son pointerı ise ekleme ve koyma işlemlerisırasında kullanılmaktadır.

typedef struct kuyrukyapisi{

        int bas,son,var,D[10];

        }KUYRUK;     

void baslangic(KUYRUK *K)

{

        (*K.var)=0;  

        (*K.bas)=0;

        (*K.son)=0; 

}

int ekle(int veri, KUYRUK *K){

    if((*K.var)>(N-1)){ puts("Kuyruk dolu"); }

    (*K.son)=((*K.son)+1)%10;

    (*K.D[(*K.son)])=veri;

    (*K.var)++;

}

int cikart(KUYRUK *K, int cikarilan)

{

    if(K.var<=0){

             puts("Kuyruk Bos");   

                 }   

    *cikarilan= (*K.D[(K.ilk)]);

    (*K.ilk)=((*K.ilk)+1)%10;

    (*K.var)--;

}

 

 

Bağlantılı liste ile kuyruk tasarımı

Kuyruğunbağlantılı liste ile tasarımında gene bir önceki modelde olduğu gibi 2 adet ilkve son elemanları gösteren pointerlar kullanılmaktadır. Ekleme işlemi son adlıverinin arkasına, çıkarma işlemi ise ilk adlı verinin alınması ve kuyruktançıkarılmasıyla yapılır. Bu yaklaşımda kuyruğun boş olması ilk değişkenin NULLolması demektir. Diğer iki yönteme göre en önemli avantajı ise bağlı listekullanılması ve dolayısıyla dinamik bellek oluşturulmasından dolayı bellekteyer oldukça yığının boyutu artabilmektedir. Kuyruk oluşumunda kullanılan eklemeve çıkarma fonksiyonları aşağıdaki gibi oluşturulabilir.

void ekle()

{

     if((p=malloc(sizeof(kuyrukdugum)))==NULL){

                 puts("Bellekte yer yok");

                                               }

     p->veri=veri;

     p->arka=NULL;

     if(ilk==NULL){

             ilk=p;

             son=p;     

                   }

     else{

             son->arka=p;

             son=p;

          }   

}

 

int cikar()

{

     if(ilk==NULL){

             puts("Kuyruk Bos");     

                   }    

     p=ilk;

     ilk=ilk->arka;

     if(ilk==NULL)

             son=NULL;

     d=p->veri;

     free(p);

     return d;

}

 

void temizle()

{

     while(ilk!=NULL){

                p=ilk;

                ilk=p->arka;

                free(p);     

                      }

     son=NULL;       

}

 

Kaynaklar

·        Data structures and program design in c, RobertL. Kruse

·        Data structures and algorithms, Adam Drozdek

·        Veri yapıları ve algoritmalar, Rıfat Çölkesen

Tags:

C dilinde yapılar

by Ordinaryus 8. February 2009 19:30

YAPI TANIMI

            Yapılar farklıtürden fakat birbiriyle alakalı veri tiplerinin bir arada tutulması içinoluşturulmuştur. Diziler konusunda aynı veri tipindeki elemanların ardışılbiçimde bellekte saklanmasında kullanılması görmüştük. Yapılar ise isim, soyisim, öğrenci numarası gibi verilerin bellekte bir bütün içerisindesaklanmasını sağlamaktadır. Bu örnekte her bir öğrenci için farklı bir yapıoluşturarak bu öğrencinin farklı veri tiplerindeki bilgilerini bir arada saklamışoluruz.

            Yaniyapılar temelde 3 önemli özellik gösterir

·        Farklı tiplerden verilerin bir arada tutulmasınaolanak sağlarlar.

·        Bellekte ardışıl olarak sıralanırlar.

·        Ve tüm bu veri tiplerine tek bir yapı ismiüzerinden ulaşılmasını sağlarlar.

Yapıların tanımlanması

 

struct <yapıismi>{

         <değişken türü>  <değişken adı 1>;

         <değişken türü>  <değişken adı 2>;

         <değişken türü>  <değişken adı 3>;

         ….

} ;

 

Buradakigösterimde struct bir anahtar sözcüktür. Yapı bildirimin mutlaka yer almasıgerekmektedir. Her yapı nesnesinin ayrıca bir ismi bulunmaktadır. Bu isimaracılığı ile yapı içerisindeki değişkenlere ulaşılabilinmektedir.

Yapıbildiriminin yapılması ile bellekte derleyici tarafından bir yer ayrılmasınaneden olmaz. Burada bir tanımlama söz konusu değildir. Bu bildirimlederleyiciye yarattığımız yeni veri türü ile ilgili bilgi verilmektedir. Yapıbildirimi yapıldıktan sonra artık bu yapının elemanları tanımlanabilir. Eleman tanımlandıktansonra derleyici yapının özelliklerine göre bellekte yer tahsis eder.

struct OGRENCImahmut;

Yazarak mahmutisimli bir nesne tanımlamış olduk bu nesnenin isim, soy isim, numara bilgilerimahmut isimli yapıda tanımlanmıştır.   

Yapı elemanlarına erişim ve değer atanması

Yapılarladiziler arasındaki farklardan biri de elemanlara erişim konusundadır. Dizilerdeelemanlara dizi ismi ve [ ] (index operatörü) yoluyla ulaşılırken yapılardaelemanlara erişim yapı nesnesinin ve elemanının ismi ayrıca nokta operatörüylesağlanır.

Nokta operatörü(.) iki operand alan bir operatördür. Bu operatörün sol tarafındaki operand biryapı türünden değişken olmak zorundadır. Sağ yanındaki operand ise bir yapıilgili yapı türünün bir elemanı olmalıdır.

 

 

 

 

struct SAYI{

int a;

            float b;

}   ;

 

main(){

struct SAYI x;

            x.a=3;

            x.b=3.14;

}

 

Veya alternatifolarak bir yapının elemanları aşağıdaki gibi de tanımlanabilir.

struct SAYIx={3,3.14};

 

YAPI NESNELERİ ÜZERİNDE YAPILABİLECEKİŞLEMLER

Yapı değişkenlerbir bütün olarak aritmetik operatörlerle ve karşılaştırma operatörleri ileişleme sokulamaz. Yapı nesneleri üzerinde aşağıdaki işlemleri yapmak mümkündür.

·        Bir yapı değişkeninin adresi alınabilir.

·        Aynı türden iki yapı değişkeni birbirineatanabilir.

·        sizeof operatörü ile bir yapı nesnesininbellekte kapladığı alan bulunabilir.

 

struct tarih{

            int gun, ay,yıl;

};

struct zaman{

            int saniye, dakika, saat;

};

struct tarih a;

struct zaman b;

a=b;    // derleme zamanında hata oluşur. a ve baynı türden değil.

 

 

Yapı türünden adres ve göstericiler

Bir yapıdeğişkenin adresi alınabilir. Bu durumda elde edilen adresin sayısal bileşeniyapının bellekteki başlangıç adresi, tür bileşeni ise yapı ile aynı türdenadrestir. Bir yapı türünden göstericiler de tanımlanabilir. Yapı türündengöstericilere aynı yapı türünden bir adres atanmalıdır.

struct ogrenci*p = &a;

p yapı türündenbir işaretçi ve numara da bu yapının bir elemanı olmak üzere erişim şu şekildeyapılır:

(*p).numara

Burada öncelikoperatörü kullanılması gerekmektedir yoksa önce p.numara ele alınır daha sonra* operatörü işleme sokulurdu. Bu da hata yapılmasına neden olurdu. 

 

Yapı dizileri

           Yapılarda programlamadabir tür olarak sayıldıklarından yapı türünden diziler de söz konusudur vesıklıkla kullanılmaktadır. Yapı dizileriyle de normal dizilerin sahip olduğutüm fonksiyonlar özellikler kullanılabilir.

            structzaman{

                        intgun, ay, yil;

};

main(){

struct zamandogumgunu[4]={{12,7,1988},{14,3,1879},{18,4,1955},{22,9,1791}};

struct zaman *pzaman;

pzaman=dogumgunu;

            for(int i=0;i<4;i++){

                                   printf(“%d-%d-%d\n”,pzaman->gun, pzaman->ay, pzaman->yil);

++pzaman;  }

}

 

 

 

Ok ( -> ) operatörü

Ok operatörü –ve > karakterlerinin beraber kullanılması ile oluşan iki operand alan biraraek operatörüdür. -> operatörünün solundaki operand adres türünden biroperand olmak zorundadır. Sağ yanında ise ilgili yapının bir elemanı olmalıdır.Sol taraftaki yapının  sağ taraftakielemanına ulaşmak için kullanılır. p yapı türünden bir nesnenin adresinitutuyor olsun.

(*p).a  ve p->a eşdeğerdir. 

 

Yapıların fonksiyonlarda kullanımı

Yapılarınfonksiyonlarda kullanılması 2 şekilde olmaktadır.

1.      Yapının  kendisinin fonksiyona parametre olarakgeçmesi

Bu yöntemdeyapıların birbirine atanabilmesi özelliğinden yararlanılır. Bu yöntemdefonksiyonun parametre değişkeni bir yapı değişkeni olur. Fonksiyonda aynı yapıtüründen bir değişken ile çağrılır. Bu aşamada atama işlemi gerçekleşir. Fakatbu yöntemde yapının elemanları blok olarak kopyalandığından hem bellek hemdezaman açısından kayıp oluşturur.

 

struct kisi{

char isim[20];

int dogumyili;

};

      void yaz(struct kisi y){

             printf(“%s-%d”,y.isim,y.dogumyili);

      }

      void main(){

               struct kisi x={“Michael Faraday”,1791};

               yaz(x);

     while(!0);

   }

              

 

2.      Yapıdeğişkenlerinin adreslerinin geçirilmesi

   Bu yöntemde ise fonksiyonunparametre değişkeni yapı türünden bir gösterici olmaktadır. Fonksiyon bu türdenbir yapı değişkeninin adresi ile çağrılmaktadır. Bu yöntem ilk yönteme göredaha kullanışlıdır ve büyük çoğunluk bu yöntem kullanılmalıdır. Bu yöntemdeyapının adresi sadece kullanıldığından bellekte israfa yol açmaz. Bu özelliğinkullanılmasında yapının bellekteki ardışıllık özelliğinden faydalanılmaktadır.

struct kisi{

char isim[20];

int dogumyili;

};

   void yaz(struct kisi *p){

               printf(“%s-%d”,(*p).isim, (*p).dogumyili);

  }

   void main(){

   struct kisi x={“Michael Faraday”, 1791};

   yaz(&x);

   while(!0);

}

 

Yapıların kullanım yerleri

1)      Algısalkolaylık sağlamak amacıyla birbiriyle alakalı olan değişkenler yapı elemanlarıolarak yapıların içerisinde saklanmaktadır.

2)      Cdilinde bir fonksiyon en fazla 4, 5 parametre almalıdır. Daha fazla parametreyesahip olması iyi bir yöntem değildir. Şayet bir fonksiyon çok fazla parametreyeihtiyaç duyuyorsa bu parametreleri bir yapı içerisinde tutmak kullanımı kolaylaştıracaktır.

3)      Cdilinde fonksiyonların tekbir geri dönüş değeri vardır. Oysa yapıkullanıldığında geri dönüş değeri yapı değişkeni türünde olabilmektedir. Bununiçin önce bilgileri yapı biçiminde ifade etmen ve yapı değişkeninin adresinifonksiyona parametre olarak vermek gerekir. Geri dönüş değeri olarak bilgileryapı değişkeninin içinde olur.

4)      Cdilindeki veri türleri sınırlarıdır. Tarih, karmaşık sayılar gibi verideğişkenleri yapılar sayesinde oluşturulabilir.

 

İÇ İÇE YAPILAR

Bir yapınınelemanı bir baksa türden bir yapının elemanı ise bu yapılara iç içe yapılar(nested structures) denir. İç içer yapıların bildirimi 2 şekilde olur:

1.          Önce eleman olarak kullanılan yapı bildirilir. Onunaşağısında diğer yapısı kapsayan yapı bildirilir.

 

struct  tarih{

int gun, ay,yil;

}

struct kisi{

char isim[20];

struct tarihdogumgunu;

} 

           int main(){

           struct kişi mahmut;

           mahmut.name=”Mahmut Tuncer”;

           mahmut.tarih.gun=32;

           mahmut.tarih.ay=13;

            mahmut.tarih.gun=1905;

         printf(“%s-%d.%d.%d”, mahmut.name,mahmut.tarih.gun, mahmut.tarih.ay,                                                                                                                            mahmut.tarih.yil);

         return 0;

         }

 

 

 

 

 

2.          Diğer yöntemde ise elemana ait yapı genel olan yapınıniçerisinde verilir.

struct kisi{

char isim[20];

struct tarih{

       int gun,ay,yil;

};

};

int main(){

struct kisix={“Mahmut tuncer”,{32,13,1904}};

}

 

 

Bir yapınınelemanı başka bir yapı türünden yapı göstericisi olabilir.

struct tarih{

int gun, ay,yil;

} ;

                     struct kisi{

                     char isim[20];

                     struct tarih *dogumgunu;        

                     };

                       

            Butanımlamada struct kisi x gibi bir tanımlama yapılmış olsun.

·        x.dogumgunu ifadesinin türü struct tarihtüründen birinden adrestir ve bir nesne belirtir. Çünkü dogumgunu bir göstericideğişkenidir.

·        x.dogumgunu->gun bir nesne belirtir ve türüint dir.

·        &x.dogumgunu->ay int türünden bir adresbelirtir.    

                 

            Biryapının elemanı kendi türünden bir yapı değikeni olmaz fakat kendi türünden biryapı işaretçisi olabilir ki bu çok kullanılan bir durumdur.

 

 

            structlist{

            inta;

            structlist *next;

};        

           

Bu yapılarözellikler bağlı listelerde ve ağaç yapıları üzerinde çalışılırken sıklıklakullanılacaktır. Veri yapıları konusunda bağlı listeler (linked list) oldukçasık karşımıza çıkacaktır. Bağlı listeler ardışıl olarak tutulmayan listelerinbağlanmasında kullanılır. Her eleman kendinden sonraki elemanın adresinisaklamaktadır. Böylece bağlı listenin tüm elemanlarına ulaşmak mümkün halegelmektedir. Son elemana ait gösterice de ise NULL adresi bırakılmaktadır.             

 

BİRLİKLER ( UNIONS )

            Birliklerdeyapılar gibi birbiri ile alakalı verilerin bir arada saklanmasını sağlartanımlanmaları ve gösterimleri aynı şekildedir ama aralarında fark yaratanönemli bir nokta vardır. Bir yapı tanımladığımızda bu yapıdaki tüm elemanlariçin bellekten ardışık olarak yer ayrılmaktaydı. Fakat birliklerde buelemanların sadece biri kullanılacağı durumlarda bellekte yer sağlamak içinbirliklere başvurulmuştur. Birliklerde derleyici birlik elemanlarını bellekteen çok yer kaplayan elemanın boyutu kadar yer ayırarak saklanmaktadır.

 

struct kisi{

            charisim;

            intyas;

            union{

                        intaskereAlınmaYili;

                        charkızlıkSoyadi;

}bilgi; 

} ;

Burada kişiyapısı içerisinde tanımlı bir birlik bulunmakta. Burada dikkat etmemiz gerekennokta birlik elemanlarının aynı anda ikisinin birden tanımlanması mümkündeğildir. Askere giden bir erkeğin kızlık soyadı olmaması, kızlarında askeregitmediğini farz edersek bu durumda bellekteki fazla yer harcamasını engellemekiçin birlik kullanmaktayız. Birlik elemanları int ve char tipindetanımlanmıştır. Bunlardan bellekte en fazla yer tutan eleman birliğin bellekteayıracağı alanı belirleyecektir. Birliğin bellekte kapladığı alan sizeof(int)olarak belirlenir. Fakat bir yapı tanımlamış olsaydık kullanmasaktasizeof(char) + sizeof(int) değeri kadar bellekten yer ayrılmış olacaktı. Bubağlamda birlik kullanmak bellekten kazanç sağlamada sıklıkla kullanılmaktadır. 

Tags:

C dilinde işaretçiler

by Ordinaryus 8. February 2009 19:28

ADRES KAVRAMI

Adres kavramı hemyazılıma hem de donanıma ilişkin bir kavramdır. Bellekteki bir veriye onunadres bilgisi ile ulaşmak mümkündür. Bellekte veriler ardışık sayısal adresbilgilerine sahip bellek gözlerinde saklanmaktadır.

 

1A00

 

1A01

 

1A02

x

1A03

 

1A04

 

1A05

 

 

            Hernesne bellekte bir yer kaplamak zorundadır bu şekilde bu nesnenin bilgisisaklanabilir. Nesnelerin adresleri sistemlerin çoğunda derleyici ve kullanılanişletim sistemine göre ortak olarak belirlenir. Nesnelerin adresini programçalışmadan önce  kesinlikle bilinemez vekullanıcı tarafından tespit edilemez. Bir nesnenin adres bilgisi programınçalışması sırasında (runtime) belirlenir. Yukarıdaki şematik gösterimde xdeğişkeni bellekte 1A03 adresini işgal etmektedir. Bu değişkenin adresideğişkenin kullanılamaya başlaması ile belirlenip görevini tamamladıktan sonrayok olmaktadır. Burada x değişkeni 1 bayt yer kaplamaktadır eğer bu değişken 1bayttan fazla yer kaplayacak olsaydı bellekte nasıl bir alan kaplardı?

 

1C00

 

1C01

 

1C02

x

1C03

1C04

 

1C05

 

1C06

 

1C07

            Buradax değişkeni bellekte 2 bayt alan kaplamaktadır. Bu değişkenin adresi ilk baytı(düşük anlamlı adres) ile gösterilmektedir. Yani adresi 1C03 tür. Eğerdeğişkenimiz long türünden bir değişken olsaydı bellekte 4 blok tahsisedilecektir. Yerel yada global olsun tanımlanan değişkenlerin bellekte ardışıkolarak tutulacağının garantisi yoktur.

Adres türleri

            Yazılımsal açıdanadres bir değişkenin bellekteki yerinin bilinebilmesi için kullanılmaktadır vebu adresteki değerin bilinebilmesi için. Ama bundan daha önemlisi eğer birdeğişkenin adresi 1 bayttan ibaret değil ise bu noktada değişkenin adresi olanilk bayt ve sonrası bellek gözleri de önem teşkil eder çünkü bu değişkeninbellekte tuttuğu alan 1 bayttan büyük ise bunun değeri sonraki baytlara dataşacaktır. Peki bir değişkenin bellekte kaç bayt yer kapladığını nasılbilebiliriz? Bu noktada bellek adresinin türü ihtiyaçlarımızı karşılayıpdeğişkenin kaç baytlık bellek gözü kapladığını ve bu değirin öğrenilmesindebize yardımcı olur. Yazılımsal olarak adres bilgisinin iki bileşeni vardır:

  • Bellekte yer gösteren sayısal değer
  • Adresin türü

 

 

Bir adresin türüdemekle o adrese ilişkin bellek bölgesinde bulunan bilginin derleyicitarafından yorumlanmış biçimi anlaşılmalıdır. C’de adres bilgisi ayrı bir veritürüdür. Daha önce gördüğümüz 11 ayrı veri türüne ilaveten bunlarınişaretçilerini de ekleyebiliriz.

GÖSTERİCİLERİN BİLDİRİMİ

Göstericileradres bilgilerini saklamak ve adres bilgileriyle işlem yapmaya yarayandeğişkenlerdir. Gösterici deyince bir nesne, adres deyince bir tür aklagelmelidir.

Göstericibildirimleri genel olarak şöyledir:

<tür>*<gösterici ismi>;

            Burada<tür> göstericinin içindeki adresin türüdür. Char, int, float..vs

            char*kelime, sozcuk;

burada kelimegöstericidir ve sakladığı adres bilgisi char türünden bir değişkene aittir.sozcuk ise char türünden bir değişkendir.

 GÖSTERİCİOPERATÖRLERİ

C toplam 4 tanegösterici operatörü vardır. Bu operatörler göstericiler ve adres bilgileriylebirlikte kullanılabilir.

Adres operatörü ( & )

            Adres operatörütek operan alan ön ek konumunda bir operatördür. Bu operanın ürettiği değeroperanın olan değişkenin adresidir. Yani operanın ürettiği adres bilgisininsayısal değeri fiziksel adres bilgisi, tür bilgisi ise değişkenin türü ile aynıtürdür. & operatörünün operandı mutlaka bir nesne olmalıdır. Çünkü yalnızcadeğişkenlerin adres değerlerine ulaşılabilir. İnteger türünden bir değişken Xtanımladığımızı farz edelim. &X adres bilgisinin iki tür içeriği vardır.Bunlardan ilk X değerinin bellekte bulunduğu yerin başlangıç adresidir, diğeriçerik ise bu verinin bellekte ardışıl kaç blokta tutulduğunu anlamaktakullanılan tür bilgisidir.

            İçerik operatörü ( * )           

            İçerikoperatörü de önek konumunda bulunan tek operan alan bir operatördür. Asteriks karakteri(*) içerik operatörü olarak kullanılmaktadır. Bir gösterici ön ek olarak *operatörünü alırsa sol tarafında kalan nesnenin bellekte sakladığı değeriverir.

            char*ptr;

            gibibir bildirim yaptığımızda buradan 2 sonuç çıkarmamız mümkündür. birincisi ptrchar türünden bir göstericidir. İçinde char türünden bir nesnenin adresinitaşır.

            İndex operatörü ( [ ] )    

            Dizi elemanlarınaulaşmakta kullandığımız [ ] operatörü aslında tek operan alan bir operatördür.[ ] arasına tamsayı türünden bir ifade yazılır fakat bu sayının pozitif olmazorunluluğu yoktur. [ ] operantının ismi bir dizi ismi olmak zorundadeğildir. Gösterici olabilir veya diğer adres belirten ifadeler olabilir. p[n]ile *(p+n) eşdeğerdir. Köşeli parantez operatörü bir adresten “n” ilersininiçeriğini almak için kullanılır.

            İŞARETÇİ ARİTMETİĞİ

            C dilinde birelemana bellekte gir alan tahsis edildiğini ve bu alanın içeriğine nasılulaştığımız gördük. Benzer bir durum dizilerde de bulunmaktadır. dizi[n] olarakbir dizi tanımladığımızı farz edelim. &dizi bizi bu dizinin ilk elemanınadizi[0]’a götürmektedir. Biz dizinin n. elemanının adresine ulaşmakistediğimizde *(dizi+n) veya &dizi+(n*sizeof(değişkentürü)) ifadesinikullanırız. Adres bilgisi tamsayı türünden bir sayı ile arttırılıp azaltılabilir.Sonuç gene aynı türden bir adres bilgisi olur. Bir adres bilgisi 1arttırıldığında adresin sayısal bileşeni adres türünün çeşidine göreartmaktadır.

main(){

            inta[5]={1,2,3,4,5};

            int*p;

            p=a;

printf(“%padresindeki elemanin degeri %ddir”,p,*p);

++p;

printf(“%padresindeki elemanin degeri %ddir”,p,*p);

return 0;

} 

++ ve –- operatörlerinin göstericilerlebirlikte kullanımı

·        ++*p durumu

x=++*p;

*p=*p+1 anlamınagelmektedir.*p bir değişken gösterdiği için değişkenin değeri 1 artacaktır.Yukarıdaki örnekte x değerine *p değişkeninin arttırılmış değeri atanacaktır.

·        *++p durumu

p göstericisinindeğeri arttırılır. Bu artış değişkenin türüne göre yapılır. Arttırılmışadresteki değere ulaşılır. 

·        *p++ durumu

++ ve *operatörlerinin ikisinin de öncelikleri aynıdır bu durumda öncelik yönü sağdansola doğrudur. Önce ++ operatörü ele alınır daha sonra p değişkenin artmamışdeğeri * operatörü ile işleme sokulup adres bilgisi alınır ve p değeriarttırılır.

x=*p++;

yazılarak x değişkenine *p değeriatanır ve p değeri arttırılır.

·        ++p[i] durumu

İndexoperatörünün önceliği ++ operatöründen fazladır. Bu nedenle önce p[i] degişkeniele alınır ve sonra bunun sakladığı değer 1 arttırılır.

p[i]=p[i]+1;

ifadesi ile aynıdır.

·        p[i]++ durumu

x=p[i]++;

burada p[i]değeri x değişkenine aktarılır ve daha sonra p[i] değeri 1 arttılır,

·        p[++i] durumu

x=p[++i];

burada önce ideğeri 1 arttırılır daha sonra dizinin bu indexse sahip elemanının değeri xdeğişkenine atanır.

·        p[i++] durumu

Burada p[i]değeri önce değişkene atanır daha sonra i değeri 1 arttırılır.

·        p++[i] durumu

x=p++[i];

Bu ifade pekkullanılan bir ifade değildir. p[i] değeri x değişkenine atandıktan sonra pgöstericisinin değeri 1 arttırılır.

 

İŞARETÇİLERİN KULLANIMI

İşaretçilerigenel olarak diziler ve fonksiyonlar konularındaki bilgilerimizi harmanlayarakkullanacağız. Bir dizi tanımladığımızı ve isminin a olduğunu farz edelim. a ile&a[0] değerlerinin aynı olduğunu biliyoruz. Böylece *(&a[0]+n) ve*(a+n) adreslerinde aynı değerdedir. Yani & ve * birbirinin aksi işlemlerdir. Derleyici dizi ifadelerini*(a+n) şekline dönüştürmektedir. 5 elemanlı bir a dizisinin elemanlarınınhepsine 0 değerini atayan bir fonksiyonun hızlı çalışmasını sağlamak içinaşağıdaki gibi yazabiliriz.

         int a[5];

for(int 0;i<5; i++){

               *(a+i)=0;

}

 

Fonksiyon parametre değişkeni olarakişaretçilerin kullanılması

Bir fonksiyonunparametre değişkeni herhangi türden bir gösterici olabilir. Fakat birfonksiyonun parametre değişkeni bir gösterici ise bu fonksiyon aynı türden biradres bilgisi ile çağrılması gerekir. Bir fonksiyonun başka bir fonksiyonunyerel değişkenini değiştirebilmesi için o fonksiyonun yerel değişkenininadresini parametre olarak alması gerekmektedir.

Bir programda ayerel bir değişken olsun eğer fonksiyon fonk(a); şeklinde çağrılmışsafonksiyonun a değişkeninin değerini değiştirme şansı yoktur. Bu tür birfonksiyon çağırmaya değer ile çağırma (call by value) denir. Eğerfonksiyonumuzun a değişkeninin değerini değiştirebilmesini istiyorsakfonk(&a); şeklinde çağırmalıyız. Böyle bir çağırma şekline adres ileçağırma (call by reference) denmektedir.

 

void fonk(*p){

               *p=20;

}            

int main(){

               a=10;

               fonk(&a);

               printf(“%d”,a);

}

 

Bir fonksiyonbir değer elde edip bunu çağıran fonksiyona iletmek isterse 2 yöntemkullanılabilir.

·        Elde edilen değer çağırılan fonksiyon tarafındangeri dönüş değeri olarak üretilir.

·        Elde edilen değer çağıran fonksiyonun göndermişolduğu adrese gönderilir. Tabi ki bunun için çağıran fonksiyonun parametredeğişkeninin bir işaretçi olması gerekmektedir.

Girilen birsayının faktöriyelini hesaplayıp bu değeri parametre olarak gönderilen adresekopyalayan bir fonksiyon yazalım.  

 

voidfaktoriyel(int n, long *p){

if(n==0 || n==1)

*p=1;

for(*p=1; n>0; n--)

*p *=n;

}

int main(){

               long a;

               int x;

               scanf(“%d”,&x);

               faktoriyel(x, &a);

               printf(“%d!=%ld”, x, a);

}

 

Dizilerden fonksiyonlara gösterici yoluylageçilmesi

 

Bir dizininfonksiyonlara geçirilmesinde dizinin başlangıç adresinin ve dizinin uzunluğununbilinmesi gerekli ve yeterlidir. Dizinin başlangıç adresini alacak değişkenindizi ile aynı türden bir işaretçi olması gerekmektedir. Fonksiyonun 2.parametresi fonksiyonun uzunluğunu tutacak int türünden bir değişken olabilir.Bir dizinin başlangıç adresini parametre olarak alan fonksiyon bu dizininelemanlarına içerik operatörünü veya köşeli parantez operatörünü kullanarakulaşabilir. Ancak dizi uzunluğunun ne kadar olduğu bilgisi fonksiyon tarafındanbilinemez bu nedenle parametre olarak alınır.

 

 

void yazdır(int*p, int i){

               for(int n=0; n<i; n++)

                          printf(“%d”, p[n]);

} 

int main(){

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

               yazdır(a,sizeof(a)/sizeof(int));

               while(!0);

}

 

Geri dönüş değeri adres biçimde olanfonksiyonlar

Bir fonksiyonparametre olarak bir gösterici alabildiği gibi geri dönüş değeri olarak da biradres türünden değere sahip olabilir. Geri dönüş değerinin adres olduğu,fonksiyon tanımlanırken * operatörü ile aşağıdaki biçimde belirtilmelidir.

<adrestürü> *<değişken ismi> (parametreler){

….

}

         C dilinde ve diğer dillerde adresedönen fonksiyonlara sıklıkla rastlanılır. Bir adres türüne geri dönen birfonksiyonun çağrılma ifadesi aynı türden bir göstericiye atanmalıdır.

            int*bul(int *p, int boyut){

                        int*pbuyuk, i;

                        pbuyuk=p;

                        for(i=0;i<boyut; i++)

                                   if(*pbuyuk> p[i])

                                   pbuyuk=p+i;

                        returnpbuyuk;

} 

int  main(){

int s[6]={3,5,-2,0,7,2};

printf(“%d”, *bul(s, 6));

 

Tags:

Powered by BlogEngine.NET 1.5.0.7
Theme by Mads Kristensen

Ordinaryus Hakkında

Hayata gözlerini İzmirde açtı. 3 yaşında legolarla oynadı =) Küçük yaşta baskete başladı zaten başka sporlarla arası hiç olmadı. Orta okulda matematikle ilgilendi. Liseyi Karşıyaka Anadolu Lisesinde okudu. 

İTÜ Elektronik Mühendisliğinden 2010 yılında mezun oldu. Fizik bölümünde çift anadala kabul oldu lisans hayatına Fizikten devam etmekte. Koç Üniversitesinde Bilgisayar Mühedisliği Master programına kabul edildi ve akademik hayatının ilk adımlarını sevdiği bir alanda çalışarak atıyor. Fizik ve Bilgisayarın ortak noktalarını gördü ve bunları geliştirmek amacıyla çalışıyor.

Yazılımı sevdi.. Başlarda herşeyle ilgilendi web programlama da yaptı, sokette programladı yeri geldi ağ yönetimi ile uğraştı. Görüntü işlemeden keyif aldı Makine Öğrenmesi ve Örüntü Tanımada kendisini geliştirmeyi istemekte.

2008te MSP oldu belkide bu blogu yazmaya başlamasında en büyük etken=) Bu görevi 2 sene boyunca sürdürdü. Bir yandan 2008de EuroSkillsde Mobil Robotik alanında Türkiyeyi temsil etti. Ardından 2009da Kanada da tekrardan yarışmacı olarak bulundu. Artık bu alanda hakemlik yaparak ve Robotino hakkında öğrendiklerini paylaşarak faydalı olmaya çalışıyor. 

Yapay Zeka, Görüntü İşleme, Kuantum Mekaniği, İstatistik Mekanik ve Bilişsel Bilimlerle ilgili. Geceleri kafasına göre takılıyo. Sabahlarıda öğrencilik yapıyo =)

gibi gibi... 

 

Page Rank

Loading

Google Translate


Şuan ne okuyorum

Bilişsel Psikoloji

Singularity is Near


Ayrıca okuduklarımdan seçtiğim kitaplara buradan ulaşabilirsiniz..

Okuduklarımı üye olarak takip etmek için ise aşağıdaki RSS bağlantısını kullanabilirsiniz. Ayrıca bana kitapta hediye edebilirsiniz =)


CCL


Copyright © Ordinaryus Says That by http://www.vypro.org/ is licensed under a Creative Commons Attribution-No Derivative Works 3.0