C’de Pointer Aritmetiği – Sıralama Algoritmaları (Bubble Sort Algoritması)

Final dönemi nedeniyle verilen zorunlu ve uzun bir aradan sonra C’de pointer aritmetiği üzerine başlattığım yazı dizisine sıralama algoritmaları ile devam ediyorum.

Bu konuya ilişkin geçmiş paylaşımlardan yola çıkarak pointer aritmetiğinin mantığını genel olarak kavradığımızı umuyorum. Bu nedenle işin pointer kısmına doğrudan kod üzerinde değineceğim. Şimdi sıralama algoritması nedir, bunu inceleyelim..

Read more

C’de Pointer Aritmetiği – Linear Search Algoritması

Pointer aritmetiği ile yapılacak  işlemlere geçmeden önce, arama algoritmaları nasıl işler ne işe yarar ondan bahsedelim.. Dizi, yığıt, kuyruk gibi veri yapılarıyla işlem yapmaya başladığınızda veri yapısına ekleme-silme işlemleri dışında en çok kafa yorulacak konu arama ve sıralama algoritmalarıdır. Arama algoritmalarıyla başlıyoruz..

Bir veri yapısında (örneğimiz için kullanacağımız dizi gibi..) arama yapabilmek için öncelikle veri yapısı içerisinde elemandan elemana istediğimiz gibi dolaşabilmemiz gerekir. Zaten diziyi dolaştıktan sonra geriye yalnızca bir karşılaştırma işlemi eklemek kalıyor ki bu da oldukça basit bir işlem..

Özetle, bir arama algoritmasının yapması gereken işlemi, “gez – karşılaştır(kontrol et) – sonuç döndür” şeklinde özetleyebiliriz. Hangi arama algoritması olursa olsun bu üç işlemi yerine getirmek zorundadır.

Şimdi bu arama algoritmaları içinden, en kolay anlaşılanı “Linear Search” ile başlayalım..

Öncelikle etkinliği hakkında birkaç şey söyleyeyim. Bir algoritmanın etkinliği pek çok duruma göre değişkenlik gösterebilir. Örneğin çok fazla kayıt içeren bir veri yapısı üzerinde arama işlemi yapıyorsak bu algoritma oldukça masraflı olacaktır. Ancak sıralı olmayan ve makul sayıda kayıt içeren bir veri yapısı için, kullanılabilecek en etkin yöntemlerden biridir.

Peki ne yapıyor bu algoritma ?

Oldukça basit.. Aradığımız elemanı, veri yapısının ilk elemanından itibaren tüm elemanlarla karşılaştırmak üzere bir döngü başlatıyor. Eğer eleman bulunursa bulunduğuna dair bir sinyal yaratıp (örneğin “bulundu” isimli değişkenin değerini 1 yapmak gibi) döngüden çıkıyor. Şayet bulunamazsa (dolayısıyla sinyal yaratılmamışsa) ve döngü bitmiş ise, elemanın dizide bulunmadığını belirtecek başka bir sinyal yaratıyor. (“Sinyal = return değerindeki değişiklik” veya “Sinyal = değişken değerindeki değişiklik” olarak düşünülebilir. Ben sinyal demeyi tercih ettim. )

C ile yazılmış algoritmayı görelim..

<h1>include <stdio.h></h1>
<h1>include <stdlib.h></h1>
//@author yigitcansener

int linear_search_ile_arama_yap(int [],int); //pointer kullanmadan doğrudan diziyi alarak arama yapacak linear search fonksiyonu..
int main()
{
int dizi[] = {1,4,3,6,5,8,2,9,11,0}; //10 elemanli bir dizi tanimladik..
int aranacak_sayi;
int bulundu=0;
printf("Aranacak sayiyi giriniz: "); //Kullanıinidan aranacak sayi istendi..
scanf("%d",&aranacak_sayi); //Sayi alindi..

if((bulundu = linear_search_ile_arama_yap(dizi,aranacak_sayi)) == -1) //Eger arama sonucu olarak -1 donerse, listede aranan eleman olmadigi anlasiliyor…
printf("Aradiginiz sayi listede yok!\n");
else
printf("Aradiginiz sayi listede %d. elemanda bulundu.\n",bulundu); // Eger donen eleman -1 den farkli ise, elemanin listede bulundugu anlasiliyor ve elemanin listedeki konumu donduruluyor..

return 0;
}

int linear_search_ile_arama_yap(int dizi[],int aranan)
{
int i;

for(i=0;i<10;i++) // Dizi geziliyor..
if(dizi[i] == aranan) // Karsilastirma islemi yapiliyor..
return i; // Eger esitlik var ise esitligin bulundugu indeks return ediliyor.. (Sinyal)

return -1; // Eger dongu sonunda hic esitlik bulunmazsa, yani icteki return hic isletilmemisse, fonksiyondan -1 dondurulerek bir ust fonksiyona dizide elemanin bulunmadigi bildiriliyor..(Sinyal)
}

Kaynak koddan da anlaşılacağı gibi işlemler oldukça basit. Şimdi işin içine pointerları karıştırıp bir arama fonksiyonu daha oluşturalım. Büyük ölçüde bu koda bağlı kalarak, hatta bu kodu düzenleyerek elde etmeye çalışalım..

<h1>include <stdio.h></h1>
<h1>include <stdlib.h></h1>
//@author yigitcansener

int linear_search_ile_arama_yap(int *,int); //Diziye ait pointer ile arama yapacak linear search fonksiyonu..
int main()
{
int dizi[] = {1,4,3,6,5,8,2,9,11,0}; //10 elemanli bir dizi tanimladik..
int aranacak_sayi;
int *ptr_dizi; // Dizinin ilk elemanini gosterecek olan pointeri tanimladik..
int bulundu=0;
printf("Aranacak sayiyi giriniz: "); //Kullanıinidan aranacak sayi istendi..
scanf("%d",&aranacak_sayi); //Sayi alindi..

ptr_dizi = dizi; //Dizinin yalnizca adini kullanarak, baslangic adresini ptr_dizi pointerine aktardik.

if((bulundu = linear_search_ile_arama_yap(ptr_dizi,aranacak_sayi)) == -1) //Eger arama sonucu olarak -1 donerse, listede aranan eleman olmadigi anlasiliyor…
printf("Aradiginiz sayi listede yok!\n");
else
printf("Aradiginiz sayi listede %d. elemanda bulundu.\n",bulundu); // Eger donen eleman -1 den farkli ise, elemanin listede bulundugu anlasiliyor ve elemanin listedeki konumu donduruluyor..

return 0;
}

int linear_search_ile_arama_yap(int *ptr,int aranan) //Pointer aritmetigi kullanarak arama yapan fonksiyon..
{
int i;

for(i=0;i<10;i++) // Dizi geziliyor..
{
if(*ptr == aranan) // Karsilastirma islemi yapiliyor..
return i; // Eger esitlik var ise esitligin bulundugu indeks return ediliyor.. (Sinyal)

ptr++;//Dizinin bir sonraki elemanina gecilmesi icin pointer degiskeninin degeri arttiriliyor..
}
return -1; // Eger dongu sonunda hic esitlik bulunmazsa, yani icteki return hic isletilmemisse, fonksiyondan -1 dondurulerek bir ust fonksiyona dizide elemanin bulunmadigi bildiriliyor..(Sinyal)
}

Görüldüğü gibi birkaç küçük değişiklik ile fonksiyonu pointerlar üzerinden işler hale getirdik..

Bir sonraki yazıda, sıralama algoritmaları ve pointer aritmetiği kullanarak sıralama algoritması tasarlama üzerinde duracağız..

C’de Pointer Aritmetiği – Fonksiyona Pointer Geçirme

Fonksiyonel programlamaya uygun olarak tasarlanacak bir programda şüphesiz ki pointer’lar da fonksiyon trafiğinden nasibini alacaktır. Çok da mantığına inmeden basitçe bunu nasıl yapabileceğimizi göstereyim;

Örneğin bir sayacımız olsun.Amacımız da bu sayacı main fonksiyonu dışında bir fonksiyonda arttırıp, main’e geri döndürmek ve değerinin değişmesini sağlamak olsun.

Bu işlemi pointer kullanmadan, değişkeni global tanımlayarak da sağlayabilirsiniz.

<h1>include <stdio.h></h1>
<h1>include <stdlib.h></h1>
void deger_arttir(int *);
int main()
{
int i;

int sayac = 0;

int *ptr_sayac;

ptr_sayac = &sayac;//Pointer 'sayac' degiskeninin adresini gosteriyor…

//Sayac degiskeni ile islem yapilmadan, fonksiyona sayac degiskeninin pointeri gonderiliyor ve deger arttirma islemi yapiliyor…
for(i=0;i<10;i++)
{
deger_arttir(ptr_sayac);
printf("Sayacin yeni degeri: %d\n",sayac);
}
return 0;
}

void deger_arttir(int *degiskene_ait_pointer) //Disaridan integer deger tutan bir hucreyi isaret eden pointer alinip, degeri arttiriliyor…
{
*degiskene_ait_pointer += 1;
}

C’de Pointer Aritmetiği – 2 (Atama İşlemleri)

Bu yazıda  da, bir önceki yazıda giriş yaptığımız ve olası hatalardan kısaca söz ettiğimiz pointerlar ile atama işlemleri konusuna devam edeceğiz.

Bu ana kadar, pointer kullanarak yapılan işlemlerde izlenecek genel yöntemlerin kavranılmış olduğunu umuyorum. Şimdi, bu yöntemleri kullanarak, bir integer dizisi tanımlayalım. Bu dizi üzerinde gezmek için bir pointer tanımlayalım ve dizimize belli bir kurala göre değer ataması yapalım…

<h1>include <stdio.h></h1>
<h1>include <stdlib.h></h1>
int main()
{
int dizi[10];
int i;
//Bir dizi veya matrisin adresini bir pointer'a atamak için o dizinin veya matrisin adini yazmak yeterlidir.
//Bir dizi veya matrisin adi, ilk elemaninin adresini gosterir…
int *dizi_gezici_ptr;
dizi_gezici_ptr = dizi;
//Pointer artik dizinin ilk elemanini gosteriyor…
<pre><code>//Simdi dizimizin elemanlarina deger atamak icin kullanacagimiz kurala gore dizimizi pointer ile gezip guncelleme yapalim...

for(i=0;i<10;i++)
{
    *dizi_gezici_ptr = i*5;
    dizi_gezici_ptr++;
}

//Eger islemimizde bir hata yoksa, dizimizin elemanlari 0 dan itibaren 5 artarak devam edecektir...
//Dizimizi yazdirip degerleri kontrol edelim...

dizi_gezici_ptr = dizi; //Pointeri yeniden ilk elemana dondurduk...
for(i=0;i<10;i++)
{
    printf("Diziye dogrudan erisim ile %d. elemanin degeri: %d\n",i,dizi[i]);
    printf("Pointer uzerinden erisim ile %d. elemanin degeri: %d\n",i,*(dizi_gezici_ptr+i));
    printf("\n");
}
return 0;</code></pre>
}

Görüldüğü gibi pointer kullanarak bir diziyi gezdik, dizi elemanlarının değerlerini güncelledik ve diziyi yine pointer kullanarak yazdırdık.

Bir sonraki yazıda, pointer kullanarak tek ve çok boyutlu dizilerde arama algoritmaları geliştirme konusu üzerinde duracağız…

C’de Pointer Aritmetiği – 1 (Atama İşlemleri)

Şimdi Pointer(İşaretçi) kullanarak bir değişkenin değerini güncelleyelim. Bir önceki örneklerimize benzer bir örnek üzerinden devam edelim…

<h1>include <stdio.h></h1>
<h1>include <stdlib.h></h1>
int sayi = 100;
int *ptr_sayi;

ptr_sayi = &sayi;
//Onceki ornegimizde buraya kadar olan islemleri çözümlemiştik.

//Simdi pointer uzerinden sayi degiskeninin degerini degistirelim.

*ptr_sayi ++; // Ornegin 1 arttiralim…
printf("%d",sayi);

//Degiskenin degerine ulasmak icin pointerin basina "*" karakterini koydugumuza dikkat edelim…

//Eger arttirma islemini

ptr_sayi++;
printf("%d",sayi);

//seklinde yapmis olsaydik, ptr_sayi pointerinin gosterdigi adreste tutulan degeri degil, gosterdigi adres degerini 1 arttirmis olacaktik…

//Ornegin "sayi" degiskeninin adresi A123 ise, ptr_sayi pointerinin gosterdigi adres A123 olacaktir. Bu adreste tutulan deger de 100'dur.

//Eger islemi "*ptr_sayi++" seklinde yaparsak, A123 adresinde tutulan 100 degeri bir artip 101 olacaktir. Ancak islemi "ptr_sayi++" seklinde yaparsak, bu kez ptr_sayi pointerinin gosterdigi A123 adresini A124 yapmis ve bambaska bir degiskeni ya da null bir bellek hucresini gostermeye baslamis oluruz.

//Not: Pointerlar ile yapilan guncelleme islemleri dogrudan bellek hucresini etkilediginden, o bellek hucresini temsil eden degiskenin de degerinin degismesine yol acar…

ptr_sayi--;
printf("%d",sayi);
//Yukarida bilerek yanlis yaptigimiz islemi geri alalim…

*ptr_sayi = 200;

//"sayi" degiskeninin gosterdigi adreste tutulan deger 200 yapiliyor…

printf("%d",sayi);

//"sayi" degiskeni yazdirildiginda ekrana 200 yazilacaktir. Cunku ayni adresi gosteren pointer ile bu adreste tutulan degeri 200 e guncellemistik…

Görüldüğü gibi pointer aritmetiği ile karmaşık işlemler yapmak için, hatta bir hocamın tabiriyle Pointer aritmetiğini “hamur gibi yoğurmak için” öncelikle temel pointer işlemlerinin mantığını kavramalıyız.

Pointer aritmetiği ile yapılan işlemlerde, unutacağınız bir yıldız karakteri bile tüm belleğinizi yanlış yönlendirmenize ve ciddi boyutta veri kaybetmenize sebep olabilir…

Bir sonraki yazıda pointerlar arasında matematiksel işlemler yaparak, bellek hücrelerinin ve o hücreleri temsil eden değişkenlerin değerlerini değiştireceğiz…

C’de Pointer Aritmetiği – Giriş (2011)

Referans tabanlı gelişmiş diller var olmadan önce, bir veriye ulaşmak için pek güvenilir olmayan ancak doğru kontroller eşliğinde bir sistemi baştan aşağı yönetebilecek erişim yetkisine sahip olmamızı sağlayan yapı, “Pointer Aritmetiği”..

Pointer aritmetiğinin genel mantığına, bir aracı ya da container olmadan veriye işaretçilerle doğrudan ulaşmak da diyebiliriz.

İşaretçi Nedir ? (Pointer)

Değişkenlere ait adres değerlerini tutan değişkenlerdir. C ‘de tanımlanırken “*” ön ekini alırlar.

Hemen kısa bir kodla örnekleyelim…

int sayi = 100;
// 100 sayisini bellege yazdik. Ve bu sayiya ulasmak icin de sayi degiskenini tanimladik.

//Eger ki bu sayiyi yazdirmak istersek;

printf("sayi: %d\n",sayi);
//kodu ile oncelikle container degiskenimize baglanip daha sonra onun uzerinden degerimize ulasiriz.

//Bir de pointer kullanarak gorelim.

int *ptr_sayi;
//Su an ptr_sayi degiskeninin, herhangi bir degiskenin adresini tutacagini derleyicimize belirttik.

//Simdi bu pointer degiskenimize, tutacagi-gosterecegi adresi atayalim.

//Not: C''de bir degiskenin adresine ulasmak icin degisken adina on ek olarak '&'  karakteri kullanilir.

ptr_sayi = &sayi;
// Artik ptr_sayi pointerimiz, sayi degiskenimizin adresini gostermekte.

//Yani biz bu sayiyi pointer araciligi ile gostermek istersek;

printf("sayi: %d (Pointer ile…)\n",*ptr_sayi);
//bu sekilde ayni sayi degerini ekrana yazdirabiliriz.

//Not: Bir degiskene on ek olarak getirilen '*' karakteri, o degiskenin icerisinde tuttugu degeri adres baz alip, o adresin icinde bulunan degere ulasilmasini saglar.

Görüldüğü gibi C’de pointer işlemleri “*” ve “&” karakterleri üzerinden yürümektedir.
Not: Kullanıcıdan veri almak için kullanılan “scanf()” fonksiyonunda da alınan değerin atanacağı değişkene ön ek olarak “&” getirip, kullanıcıdan alınan değeri,bu değişkenin temsil ettiği adrese yazdığımızı hatırlayalım…

Şimdi işin içine bir de pointer karıştırdığımızda, neler olduğunu görelim…

Buraya kadar “*” ve “&” kullanımı ile ilgili aklımızda bir şeyler şekillendi ise, artık pointer aritmetiği örneklerine geçebiliriz…