Pointers ( İşaretçiler) #2 – C Programlama Dili
Bir önceki dersimizde pointer konusunun temelleri ve mantığı üzerinde konuşmuştuk. Bu dersimiz o dersin devamı niteliğindedir.
Derse geçmeden önce pointerlar ile alakalı olarak önemli gördüğüm bazı şeylerden bahsetmek istiyorum.
İlk derste C dilini güçlü yapan özelliklerinden birisi "pointer erişimine imkan veriyor olmasıdır" demiştim. Her programlama dilinde pointer kullanmak mümkün değil çünkü.
Özellikle veri yapıları ile ilgilenirseniz sıklıkla pointer kullanmanız gerekecek. Pointerlar, en basit haliyle RAM üzerindeki bir alanı bir değişken yoluyla göstermek demektir.
Pointerları kullanarak tek bir hafıza ünitesini başka ünitelere bağlayarak komplex bir yapı oluşturabilirsiniz.
Özetle pointerlar, hafızadaki bir alanın programcı tarafından kontrol edilmesini sağlar. Şimdi derse geçelim.
Pointerlarla İşlemler
Pointer dersinin bu bölümünde pointerlar üzerinde yapılan işlemlerden bahsedeceğiz.
Atama İşlemleri
Pointerlarda atama işlemleri ile başlayalım.. Bir önceki derste bununla alakalı örneklere yer vermiştik. Atama işlemleri iki yönlü yapılabilir.
int *iptr , ivar;
*iptr = 55; // bu atama ivar = 55; anlamına gelmektedir.
Bu ifadeyle pointer üzerinden değişkenin değerini değiştirmiş olduk. Bu ifadenin tam tersi de kullanılabilir :
ivar = *iptr;
şeklinde. Görüldüğü gibi pointerlar eşitliğin her iki tarafında da bulunabilirler. Uygulamasını görelim :
İlk başta int tipinde x, y, ve z değişkenlerini tanımladık. Bir de int tipinde adı point olan bir pointer değişken tanımladık.
point adındaki pointer ilk olarak x’in adresini gösterdi :
point = &x;
Aşağıdaki ifade ise *point içeriğini z değişkenine atamış oldu. Aslında burada yapılan z = x; den başka bir şey değildir.
z = *point;
Sonrasında ise printf() fonksiyonu ile değişkenlerin yeni değerlerini ekrana yazdırmış olduk.
Programın ilerleyen satırlarında ise aynı pointer bu sefer y değişkeninin adresini işaret etmiştir :
point = &y;
Aşağıdaki kod ile ise bu sefer de y değişkeninin içeriği z değişkenine atamıştır.
z = *point;
Bu uygulamada görüldüğü gibi bir pointer uygulama boyunca farklı farklı adresleri gösterebilir.
Çıktımız aşağıdaki gibidir :
Aritmetik İşlemler
Pointerlar ile aritmetik işlemler yapabilirsiniz.
Pointerlarla ilgili olarak “ & “ ve “ * ” operatörlerini kullanmıştık. Aritmetik işlemlerde ise sadece 4 operatörü kullanabiliriz : “ + “, “ - “, “ ++ “ ve “--“
Basit bir uygulama üzerinden gidelim :
Uygulamanın ilk kısmı, daha önceki derslerde belirttiğimiz şekilde ilk değer atamalarıdır. ivar int değişkeninin adresi ipt pointer değişkenine atanmıştır.
Sonrasında gelen printf() fonksiyonu ile ipt pointerının işaret ettiği adresin değeri (*ipt) yazdırılmıştır :
printf("ivar : %d\n\t", *ipt);
Buraya kadar uygulamanın çıktısı aşağıdaki gibidir :
Sonrasında gelen
ivar = *ipt + 5;
kodu ile pointer ifadesi üzerinde toplama işlemi yapılmıştır. Toplama işlemi basit olsa da pointerlar ile alakalı kavramları bilmediğiniz takdirde anlamlandırmak güç olabilir.
Bu ifadede *ipt ifadesinin ivar değişkeninin değerini ifade ettiğini bilmemiz gerekiyor. Yani bunu toplama ifadesinde yerine koyarsak,
ivar = 9 + 5 ; // Yani 14 değerine sahip olması gerekir.
Sonraki printf() fonksiyonlarında ise ivar değişkeninin değerini ekrana yazdırarak bir nevi kontrol yapıyoruz. Çıktımız aşağıdaki gibidir :
Pointer kullanırken, bazen pointerın gösterdiği adresten önceki ya da sonraki adreslere erişmek istenebilir. Pointerlar üzerindeki aritmetik işlemler özellikle böyle durumlar için kullanılır.
Şimdi farklı bir uygulama yapalım.
Bu uygulamada char, int ve double tipinde 3 farklı değişken tanımladık : ca, ib,dc
Bu değişkenlerin adreslerini tutması için char, int ve double tipinde 3 farklı pointer değişken tanımladık: cpta, iptb, dptc şeklinde.
Sonrasında tiplerine uygun olacak şekilde adres atamalarını yaptık:
cpta = &ca;
iptb = &ib;
dptc = &dc;
ve pointerların tuttukları adresleri ekrana yazdırdık.
Aşağıdaki kodlarda ise bu adreslerin değerlerini ++ ve – – operatörlerini kullanarak değiştirdik:
cpta--;
++iptb;
dptc++;
Adres değerlerini görmek için pointerların tuttukları adresleri yazdırdık:
printf("\n\tcpta : %p, iptb : %p, dptc : %p", cpta, iptb, dptc);
Buraya kadar olan çıktımıza bakalım :
Görüldüğü gibi adres değerlerini ++ ve -- operatörleri ile değiştirmiş olduk. Peki değişkenlerin adresleri ne durumda?
Onlar aynen duruyor, pointerlarımız artık başka adresleri tutuyor.
Peki bir pointer başka bir pointerı gösterebilir mi?
Pointerlar, değişkenleri gösteren yapılardı. Pointerların da aslında birer değişken olduğunu düşünürsek bir pointer başka bir pointerı tabii ki gösterebilir.
Normal değişken bildiriminden tek farkı pointer isminin başına tek “ * ” değil “ ** ” getirmektir.
Bu yıldız sayısı değişebilir duruma göre. Mesela pointerı işaret eden pointerı tanımlayacaksak ** yıldız kullanabiliriz.
Ancak pointerı işaret eden bir pointer işaret edecek bir pointerı tanımlayacaksak *** yıldız kullanılır. Tekerleme gibi oldu. Görseli aşağıdaki gibi olabilir. Kabaca işin içinde 3 pointer varsa 3 yıldız gerekecek diyebiliriz.
Şimdi basit bir örnek yapalım. Örneğimizde aşağıdaki resim üzerinden gideceğim.
Aşağıdaki uygulamanın kod kısmını incelerken bu resme bakarsanız her şey daha kolay anlaşılacaktır.
Kodlarımız aşağıdaki gibidir :
Uygulamamızın başında
int *ipt1, **ipt2, ivar = 5;
kodu ile int tipinde ipt1, ipt2 isimlerinde iki pointer ve ivar adında değeri 5 olan bir int değişken tanımladık.
Sonrasında
ipt1 = &ivar;
ivar değişkeninin adresi olan adres1’i ipt1’in içine attık. Yani ipt1 pointer değişkeninin içinde adres1 var. Aşağıdaki kod ile de bu değerleri ekrana yazdırdık.
printf("\n\tPOINTER, POINTERI GÖSTERIR MI?\n");
printf("\n\tivar : %d", *ipt1); //ivar değişkeninin değeri
printf("\n\t&ivar : %p\n", ipt1); //ivar değişkeninin adresi
Sonrasında gelen
ipt2 = &ipt1;
kodu ile ipt1 pointerının adresini ipt2 pointerının içerisine attık. Yani şekil üzerinden gidecek olursak ipt1 ‘in adresi olan adres2 , şuan ipt2 nin içerisindedir.
Aşağıdaki printf() fonksiyonlarına bakalım :
ipt2 : %p , ipt2 ; // Bu ifade ipt2 pointerının içeriğini bize verecektir yani adres2 bilgisini
*ipt2 : %p , *ipt2; // Bu ifade bize ipt2 pointerının işaret ettiği adresin içindeki değeri ifade ediyor ki bu da adres1 dir.
**ipt2 : %d, **ipt2; // Bu ifade bize ipt2 pointerının işaret ettiği ipt1 in içindeki adresin içindeki değeri gösteriyor, yani 5 değerini.
Benim kabaca çizdiğim kutucuğa bakarsanız ipt2’nin yanındaki tek yıldızla ipt1’in içindekine, 2 yıldızla da adres1 içindeki değere erişmiş oluyorsunuz.
Çıktımız aşağıdaki gibidir :
Pointer operatörleri ile alakalı olarak bilinmesi gereken en önemli şey önceliktir.
* ve ++ operatörlerinde öncelik sırası sağdan sola doğrudur:
*pointer++; // Bu ifade ile pointerın gösterdiği bellek adresi artırılıyor.
(*ipointer)++; //ifadesi ile de Pointerın gösterdiği bellekteki değişken değeri artırılıyor.
Uygulaması aşağıdaki gibidir :
Çıktımız ise :
Pointer ve Diziler
Pointer dediğimiz göstericilerin diğer önemli kullanım alanı ise dizilerdir. Dizilerin RAM'de ardışık halde bulunan veri üniteleri olduğunu belirtmiştik. Diziyle RAM de istediğimiz adrese doğrudan erişebiliriz.
Bu haliyle
Her dizi bir pointer, her pointer da doğal bir dizidir.
Pointerların dizilerden tek farkı, diziyi tanımlarken boyutu sabit olarak belirtmemiz gerekir. Ancak pointerlarda bunu yapmamıza gerek yoktur.
Pointerı siz bir dizi olarak kullanabilirsiniz ve hafızada ne kadar yer kaplaması gerektiğini daha sonra ayarlayabilirsiniz. (Bu konu bir sonraki derste verilecek olan "Dinamik Hafıza Kullanımı" başlığında ele alınacaktır. )
Dizilerdeki köşeli parantez operatörü [], pointerlardaki * yıldız operatörü gibidir.
Bunu basit bir ifade üzerinde görebiliriz;
int dizi[5], *p;
p = dizi; // p pointerının içerisine dizinin adresini atıyoruz
Böyle bir ifade için dizinin 5. elemanına erişmek isteyelim:
dizi[4] veya *(p+4)
aynı anlama gelmektedir.
Şimdi dizi ve pointerlar arasındaki ilişkiyi gösteren basit bir uygulama yapalım :
Uygulamada int tipinde adı ipt olan bir pointer ve int tipinde adı array olan 5 elemanlı bir dizi tanımladık.
Uygulamanın başında ipt pointerının kendi adresi ekrana yazdırılmıştır(&ipt) .
Aşağıdaki kod ile
ipt = array; // ipt = &dizi[0] da diyebilirdik aynı anlama gelir
dizinin başlangıç adresi, ipt pointerına atanmıştır. Dizilerde dizileri gösterecek pointerlara dizilerin ilk elemanının adresi verilir. Burada array[5] dizisinin ilk elemanının adresi array ya da &array[0] dır.
Dizilerin isimleri aslında onların başlangıç adresidir. Bu sebeple dizi_adi ya da ilk elemanın adresi &dizi_adi[0] aynı anlama gelmektedir.
dizi_adi = &dizi_adi[0]
Pointera adres ataması yapıldığına göre ilerleyebiliriz. Karşımıza bir for döngüsü geliyor. Bu döngüyü diğerlerinden ayıran artırma bölümünde iki ifadenin bulunması.
i adındaki int değişken for döngüsünün kontrol elemanıdır ve dizi eleman sayısı kadar sayısı artacaktır. Bunu da i++ ifadesinden anlıyoruz. Dizi eleman sayısı artarken aynı zamanda ipt++ ile de pointerın adres değeri artırılmaktadır.
Aşağıdaki for döngüsünde ayrıca her bir iteration için ipt pointerının adresi ve her bir dizi elemanının adresi ve değeri ekrana yazdırılmıştır.
for(int i = 0; i < 5 ; i++, ipt++)
{
printf("\n\t");
printf("Pointerın gösterdiği adres : %p", ipt);
printf("\n\t");
printf("%d.dizi elemanının adresi : %p", i+1, &array[i]);
printf("\n\t");
printf("----------------------------------\n\t");
printf("\n\t");
printf("ipt Pointerın gösterdiği adresteki değer : %d", *ipt);
printf("\n\t");
printf("%d.dizi elemanının değeri : %d", i+1, array[i]);
printf("\n\t");
printf("----------------------------------\n\t");
}
Çıktımız aşağıdaki gibidir :
Görüldüğü gibi pointerın gösterdiği adres ile dizi elemanlarının adresi birebir örtüşmektedir.
Şimdi de dizi elemanlarına pointer ile erişebileceğimiz basit bir uygulama yapalım :
Uygulamada önemli olan kısım dizi elemanlarına erişimde kullanılan ifadedir :
*(point+i); // ifadesi ile bir sonraki adresin içeriğine erişilmeye çalışılmıştır.
array[i] ifadesi ile *(point+i) ifadesi aynı şeyi ifade etmektedir.
Çıktımız ise aşağıdaki gibidir :
Her pointer aslında doğal bir dizidir, ifadesini kullanmıştık. Bu açıklamayı destekler nitelikte bir uygulama yapalım. Bu uygulamada pointer indexleme işlemi kullanılmıştır :
Görüldüğü gibi pointera tıpki dizilerdeki gibi erişim sağlanmıştır.
Çıktımız aşağıdaki gibidir :
Son olarak karakter dizilerinde pointer kullanımını gösteren bir örnek yapalım.
Karakter dizilerine ilk değer atamasının çift tırnak içerisinde yapıldığını görmüştük. Pointerlar da aslında doğal diziler olduğundan char tipindeki bir pointera da aynı şekilde atama yapabiliriz.
Çıktımız aşağıdaki gibidir :
Karakter dizileri, hem dizi hem de pointerlar ile kullanılabilir.
Bugünlük bu kadar, umarım faydalı bir çalışma olmuştur.
Uygulama noktasında herhangi bir sorun yaşarsanız aşağıdaki yorum panelini kullanarak iletişime geçmekten çekinmeyin lütfen. Teşekkürler.
Kaynak belirtilmeyen görseller @etasarim a aittir. / Pictures that don’t have any source belongs to @etasarim.
Posted from my blog with SteemPress : http://etasarim.cloudaccess.host/21/03/2019/pointers-isaretciler-2-c-programlama-dili/
Congratulations @etasarim! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :
You can view your badges on your Steem Board and compare to others on the Steem Ranking
If you no longer want to receive notifications, reply to this comment with the word
STOP
To support your work, I also upvoted your post!
Do not miss the last post from @steemitboard:
Hi, @etasarim!
You just got a 1.65% upvote from SteemPlus!
To get higher upvotes, earn more SteemPlus Points (SPP). On your Steemit wallet, check your SPP balance and click on "How to earn SPP?" to find out all the ways to earn.
If you're not using SteemPlus yet, please check our last posts in here to see the many ways in which SteemPlus can improve your Steem experience on Steemit and Busy.
Bir pointer'ın bir pointer ı gösterebildiğini bize göstermiş olduğun için teşekkür ederiz :)
Congratulations! This post has been upvoted from the communal account, @minnowsupport, by etasarim from the Minnow Support Project. It's a witness project run by aggroed, ausbitbank, teamsteem, someguy123, neoxian, followbtcnews, and netuoso. The goal is to help Steemit grow by supporting Minnows. Please find us at the Peace, Abundance, and Liberty Network (PALnet) Discord Channel. It's a completely public and open space to all members of the Steemit community who voluntarily choose to be there.
If you would like to delegate to the Minnow Support Project you can do so by clicking on the following links: 50SP, 100SP, 250SP, 500SP, 1000SP, 5000SP.
Be sure to leave at least 50SP undelegated on your account.
Bu yazı Curation Collective Discord Sunucusunda küratörlere önerilmiş ve manuel inceleme sonrasında @c-squared topluluk hesabından oy ve resteem almıştır.
This post was shared in the #turkish-curation channel in the Curation Collective Discord community for curators, and upvoted and resteemed by the @c-squared community account after manual review.
@c-squared runs a community witness. Please consider using one of your witness votes on us here
Thanks for support