C++'ta daha önceleri gördüğümüz değişken yapısı ile bir ada sahip değişkenin değerine ulaşmak için değişkeni çağırmamız yeterliydi. Bu konu itibari ile değişkenlerin bellekte saklandıkları konumlara erişmek ve müdahalede bulunabilmenin nasıl olduğunu göreceğiz.
C++'ta oluşturduğumuz her değişken yalnızca ismi ile çağırılırdı. ad1 ve ad2 adında oluşturduğumu 2 değişken bellekte arka arkaya sıralanmaz, farklı konumlarda saklanır ve çağırıldığında doğrudan erişilir. Yani örneğin ad1 adındaki değişkenimiz bellekteki 1552 numaralı konumda ise ad2 1553 numaralı konumdadır diyemeyiz!
Öte yandan işaretleyiciler için konu biraz daha farklıdır. Yani oluşturulan her yapı birbirini takip eder. Bir integer dizi düşünün. Dizinin içerisinde ilk nesnesi 1552 numaralı konumda ise 2. nesnesi bundan 4 baytlık uzaklıkta olan 1553 konumundadır. Olayı daha da genişletirsek;
1552 numaralı adresin bir öncesi 1551, bir sonrası 1553, 100 aşağısı 1452 diyebiliriz. Hatırlayacağımız üzere bunu değişkenlerde söylememiz mümkün değil.
Operatör Adresi (&)
Bir değişkenin bellekteki adresine doğrudan erişmek için kullanılan operatördür. Şu şekilde kullanabiliriz;
b = &a;
Normal kullanımını düşünelim. Eğer & işareti olmadan b = a yapsaydık a değişkeninin sahip olduğu değeri b değişkenine atamış olurduk. Burada ise yaptığımız işlem a değişkeninin adresini b değişkenine atamak. Yani diyoruz ki a'nın bellekteki konumunu b değişkenine ata..
Bir örnekle devam edelim.
sayi = 25;
konum = &sayi;
sayi2 = sayi;
Bu yapıda belirtilen sayi değişkeninin adresini örneğin 1552 olarak kabul edelim (Normalde atanmadan önce bunun bilinmesi imkansızdır, kavranabilmesi amacıyla belirtiyorum).
Yukarıda tanımladığımız sayi,konum ve sayi2 değişkenlerinin değerlerini yukarıdaki görselde görebiliriz. sayi2 = sayi kısmını zaten klasik olarak diğer tüm derslerde de yaptığımız gibi bir atama işlemi kabul edebiliriz. Öte yandan başına & işareti ekleyerek konum değişkenine yaptığımız atama, atanan değerin konumu belirtmektedir.
Adres Çözümlemesi (*)
"Reference" işleminin tersi olarak "Dereference" ismi ile anılan işlemdir. Yani referans alınan adrese göre değere ulaşmaya yarar. Yukarıda yaptığımız işlemin tam olarak tersi diyebiliriz. İşaret olarak şöyle bir kullanımdan söz etmemiz doğru olacaktır.
b = *a;
Şimdi örneğimizi tekrar ele alarak işlemimize devam edelim.
Eğer biz konum değişkeninde saklanılan konumun değerini adres kabul ederek sayi2'ye atarsak aslında sayi2 = sayi işlemini yapmış oluyoruz. Yani;
sayi2 = *konum;
*konum ibaresi bize tam olarak "Adresi 1552 olan değer" ibaresini temsil edecektir. Bu sebeple bellekte adresi 1552 olan alana gidilecek ve orada varolan 25 değerini alıp sayi2 değerine atayacaktır.
Burda dikkat edilmesi gereken 2 önemli nokta vardır. Değişkenlerin neleri temsil ettiğini iyi görmek gerekiyor. Örneğimiz üzerinden devam ediyorum;
sayi2 = *konum
Bu bize 25 değerini verir.
sayi2 = konum
Bu bize 1552 değerini verir.
Reference - Dereference
& - Adres operatörüdür ve değişkenden önce yazılarak yazılan değişkenin adresini verir.
* - Adres çözümleme operatörüdür ve değişkenden önce yazılarak değişkende belirtilen değeri alır ve aldığı değerle eşdeğer olan adrese gidip adresteki değeri döndürür.
Bu bilgilere göre bir kez daha kanıtlanıyor ki & ve * birbirinin tersidir. Öyleyse;
sayi = 25;
konum = &sayi;
şeklinde yapılan bir tanımlama ile aslında aşağıdaki gibi bir sonuca gidiyoruz;
sayi => 25
&sayi => 1552
konum => 1552
*konum => 25
Bakıldığında;
- ilk satırın doğru olduğu gözlemleniyor. sayi isimli değişkenimize 25 değerini atamıştık ve bu sebeple açık bir şekilde sayi 25'tir deriz.
- İkinci satırda & işareti bir adres operatörü demiştik. Öyleyse sayi değişkeninin adresini alacaktır. sayi değişkeninin adresi de örneğimizde belirttiğimiz 1552'dir. Dolayısıyla burada da bir problemimiz yoktur.
- Üçüncü satırda ise konum değişkenine sayi değişkeninin adresini doğrudan atadığımız için bir problem gözükmemektedir. Adresi 1552 olarak öğrendiğimiz için doğrudan konum değişkeninin 1552 olduğunu gözlemliyoruz.
- Son satırda * işareti bir adres çözümleme operatörüydü. Öyleyse 1552 adresini çözümlemesini istiyoruz. 1552 adresindeki değeri döndüreceği içinde doğrudan 25 olduğunu görmüş olduk.
Tüm bu olaylardan anlıyoruz ki;
sayi == *konum
İşaretçilerin Tanımlanması
İşaretçilerin programa tanıtılması için kullanılması gereken yapı klasik olarak;
veriTipi * isaretciAdi;
şeklindedir. Burada dikkat edilmesi gereken bölüm veri tipidir. Oluşturulan işaretçinin temsil edeceği değişkenin veri tipi ile aynı olmalıdır. Yani kendi değil işaret ettiği değişkenin türü bizim için önemlidir.
Basit bir yapıya sahip işaretçi örneğini inceleyebiliriz;
int main() {
int sayi;
int * isaretci;
isaretci = &sayi;
*isaretci = 10; // sayi = 10 ile aynı anlama geliyor.
cout << sayi;
}
Ekrana 10 çıktısını verecektir.