Pointers - İşaretleyiciler

GO programlama dilinde her değişken hafızada bir yer tutar bu değişken için sistem hafızasında geçici ve benzersiz bir atama yapılır. Bir değere ilk atanan benzersiz hafıza ifadesine erişmek için o değere ait pointer ifadesini bilmemiz gerekir.

Örnek üzerinde inceleyelim :


func BasicPointers() {
	var a int = 55 // Bir tamsayı değişkeni bildir
	var p *int     // Bir tamsayıya işaret eden pointer bildir

	p = &a // 'a'nın adresini 'p'ye ata

	fmt.Println("a'nın değeri:", a)  // 'a'nın değerini yazdır
	fmt.Println("a'nın adresi:", &a) // 'a'nın adresini yazdır
	fmt.Println("p'nin değeri:", p)  // 'p'nin değerini (a'nın adresi) yazdır
	fmt.Println("p'deki değer:", *p) // 'p'yi dereference ederek 'a'nın değerini al

	*p = 33                              // 'p'nin işaret ettiği adresteki değeri değiştir
	fmt.Println("a'nın yeni değeri:", a) // 'a'nın değiştirilmiş değerini yazdır

}

DIKKAT: pointer ifadelerinde declaration ve initialization kavramları için iki farklı sembol yer alır:

  • & ifadesi : initialization ifadesidir. Yani pointer ifadesine ait adresi başlatmak için kullanılır.
  • '*' ifadesi : declaration ifadesidir. Bir ifade önüne bu konulduğunda bu ifadenin pointer olarak kullanılacılağını belirtir.

Yukarıdaki fonksiyonu inceleyeliyelim. Öncelikle a isimli int tipinde 55 değerinde bir değişken tanımladık ve GO bu değişken için bir hafıza birimi atadı. Daha sonra p isimli bir pointer ifadesi tanımladık ve bu ifadenin int türünde bir pointer ifadesi olduğunu var p *int biçiminde yazdık. Yani p ifadesi bir hafıza adresi görüntüleyecektir. Fakat mevcut tanımlı bir hafıza ataması olmadığından dolayı p = &a ifadesiyle a değişkenin hafıza değerini p isimli pointer'a atadık. Kodumuzu çalıştırdığımızda ekrana aşağıdaki gibi bir çıktı gelecektir.

a'nın değeri: 55
a'nın adresi: 0xc0000a6010
p'nin değeri: 0xc0000a6010
p'deki değer: 55
a'nın yeni değeri: 33

Bu ifadeleri eşleştirelim :

fmt.Println("a'nın değeri:", a)
//a'nın değeri: 55

fmt.Println("a'nın adresi:", &a)
// a'nın adresi: 0xc0000a6010

fmt.Println("p'nin değeri:", p)
// p'nin değeri: 0xc0000a6010

fmt.Println("p'deki değer:", *p)
// p'deki değer: 55

Yukarıdaki kod bloğunu incelediğimizde &a ifadesi 0xc0000a6010 gibi eşsiz bir hafıza konumunu görüntüler. Bu ifade sistemde a değişkeni için ayrılan hafıza bloğunun adresidir. a ifadesine 55 değerini atamıştık.

Hatırlarsanız ilk işlemde :

	p = &a // 'a'nın adresini 'p'ye ata

şeklinde a'nın hafıza adresini p değişkenine atamıştık. Bu yüzden p değişkeni çağrıldığında hafıza adresi görüntüleyecektir.

fmt.Println("p'nin değeri:", p)
// p'nin değeri: 0xc0000a6010

Eğer p isimli pointer ifadesinin önüne asteriks koyarak *p şeklinde yazarsak, gerçekleşen işlem şöyle olacaktır. p değişkeni GO 'daki adrese gider, bu adresteki atanan değeri alır ve ekrana yazdırır . Bizim işlemimizde p değerine a ya ait adres atanmıştı. p isimli pointer ifadesi o adrese gider ve o adresteki 55 atammış değerini bize döndürür.

fmt.Println("p'deki değer:", *p)
// p'deki değer: 55

Fonksiyonlarda Pointer İfadeleri

Fonksiyonlarla pointer ifadeleri kullanılırken hafıza değer atamaları noktasında sıkça karşımıza çıkacak ifadeler mevcuttur. Fonksiyon argümanlarına pointer atamaları ya da fonksiyon çıktılarına pointer atamaları ile karşılacağız. Yazım biçimi aşağıdaki gibidir.


// func funcname(variable *datatype){}
func FonksiyonAdı(mypointer *string){}

Şimdi bir örnekle bu atamayı açıklayalım. İki adet fonksiyon oluşturuyoruz ve bu fonksiyonları main() isimli fonksiyonuzda çağıracağız.

func PointerDegerDegistirici() {
	a := 58
	fmt.Println("Fonksiyon çağrılmadan önceki a değeri =", a)

	fmt.Println("DegerDegistir isimli Fonksiyon çağrıldı.")
	DegerDegistir(&a)

	fmt.Println("Fonksiyon çağrıldıktan sonraki a değeri =", a)
}

func DegerDegistir(num *int) {
	*num = 99
}

Yukarıdaki örnekte DegerDegistir(num *int) isimli fonksiyonumuz a değişkenine ait adrese yeni bir değer atamaktadır. a değeri PointerDegerDegistirici() isimli fonksiyonumuzda 58 olarak belirlenmişti. &a ile bu değer atamasının hafızadaki konumu bulunur. Bu değeri DegerDegistir(&a) biçiminde ikinci fonksiyonumuza verdiğimizde buradaki değer ifadesi num *int formatına çevrilir. Yani hafızadaki 58 değeri * işareti ile işleme tabi tutulacak bir hale getirilir. *num = 99 ifadesiyle bulunduğu adresteki değerine 99 değeri atanır. Böylece sistem hafızasında a ifadesinin yeni değeri artık 99 olmuştur.

package main

import "fmt"

func main(){
  PointerDegerDegistirici()
}

func PointerDegerDegistirici() {
	a := 58
	fmt.Println("Fonksiyon çağrılmadan önceki a değeri =", a)

	fmt.Println("DegerDegistir isimli Fonksiyon çağrıldı.")
	DegerDegistir(&a)

	fmt.Println("Fonksiyon çağrıldıktan sonraki a değeri =", a)
}

func DegerDegistir(num *int) {
	*num = 99
}

Bu ifademizi çağırdığımızda aşağıdaki çıktıyı elde edeceğiz:

Fonksiyon çağrılmadan önceki a değeri = 58
DegerDegistir isimli Fonksiyon çağrıldı.
Fonksiyon çağrıldıktan sonraki a değeri = 99

struct ifadelerinde pointer Kullanimi

struct ifadelerinde pointer kullanımı genellikle struct ifadesine ait bir özelliğe harici bir yapıyla erişip değiştirmek amacıyla kullanılır.


package main

import (
    "fmt"
)

type Sahis struct {
    isim string
    yas  int
}

func main() {
    kisi1 := Sahis{"Cemil", 35}
    fmt.Println("Fonksiyon çağrısından önce, kisi1 =", kisi1)

    DuzenleSahis(&kisi1)

    fmt.Println("Fonksiyon çağrısından sonra, kisi1 =", kisi1)
}

func DuzenleSahis(kisipointer *Sahis) {
    kisipointer.yas = 32 // Kişinin yaşını değiştir
  kisipointer.isim = "Esra"
}

Yukarıdaki örnekte Sahis isimli isim ve yas özellikleri olan bir struct yapısı kurulmuştur. kisi1 isimli bir struct nesnesi oluşturduk. Bu şahsın isim niteliği "Cemil" ve yas niteliği 35 olarak tanımlandı. Simdi bu sahsin isim ve yas ozelliklerini pointer yardımıyla degistirelim.

    DuzenleSahis(&kisi1)

Bu ifade kisi1 isimli öğeye ait adres değerine erişim sağlar. Bu yapı bir struct yapısı olduğundan dolayı kisi1.isim ve kisi1.yas niteliklerine erisim sağlar. Aşağıdaki fonksiyon aracılığıyla kişi1 nesnesine ait isim ve yas niteliklerini değiştirelim.


func DuzenleSahis(kisipointer *Sahis) {
  kisipointer.yas = 32 // Kişinin yaşını değiştir
  kisipointer.isim = "Esra"
}

Kodumuzu calistirdiğimizda aşağıdaki ekran çıktısını elde edeceğiz.

Fonksiyon çağrısından önce, kisi1 = {Cemil 35}
Fonksiyon çağrısından sonra, kisi1 = {Esra 32}

struct niteliklerini teker teker değiştirebileceğimiz gibi hepsini birden de örnekte olduğu gibi değiştirdik.

new ve make ifadelerinde pointer

nil ifadeler oluşturmaya yarayan new ve make gibi ifadeler de sistemde hafızada yer kaplar ve pointer ifadeler kullanılarak bu ifadelere de atamalar yapılabilir.


package main

import (
    "fmt"
)

func main() {
	sayi := new(int)                                    // Bir tamsayı için bellek tahsis et ve ona bir pointer döndür
	fmt.Println("sayi'nin adresi:", sayi)               // adres : new(int) isimli degiskenin sayi icin ayırdığı adres
	fmt.Println("sayi'ya tahsis edilen adresi:", &sayi) // DIKKAT : new(int) Adresine tahsis edilen adres
	fmt.Println("sayi'daki mevcut değer:", *sayi)       // Varsayılan değer (0)
	*sayi = 100                                         // sayiya 100 degerini ata
	fmt.Println("sayi'daki yeni değer:", *sayi)         // atanmis degeri goruntule
	fmt.Println("sayi'nin ilk adresi : ", sayi)         // sayinin ilk adresi
}

Yukarıdaki örnekte sayi := new(int) ifadesi ile hafızada int tipinde değerler alabilen yer ayrılmış bir sayi degiskeni olusturduk.

Ekran çıktısı aşağıdaki gibi olacaktır.

sayi'nin adresi: 0xc000114010
sayi'ya tahsis edilen adresi: 0xc000116018
sayi'daki mevcut değer: 0
sayi'daki yeni değer: 100
sayi'nin ilk adresi :  0xc000114010

şimdi pointer ifadelerimizi inceleyelim. Öncelikli new ifadesi yeni oluşturulan degisken için bir pointer adresi tahsis eder. Bu pointer adresi, sayi isimli değişkenin adresinden farklıdır. 0xc000114010 adresi pointer icin tahsis edilen adrestir. 0xc000116018 adresi ise bu değişkene değişken olduğundan dolayı tahsis edilen hafıza adresidir. Yani iki farklı hafıza değeri olmasının sebebi, sayi isimli değişken için hafızada bir değer ve sayi değişkeninin hafızada tahsis ettiği pointer ifadesi için başka bir değer oluşmuştur. pointer ifademize *sayi = 100 şeklinde yaptığımız atamadan dolayı sayi ifademizin yeni değeri 100 olmuştur.