---
title: İşaretçiler
source: 'https://academia.sh/tr/kurslar/go-temelleri/isaretciler'
course: 'Go Temelleri'
language: tr
updated: '2026-08-23T14:25:00+00:00'
license: 'CC BY-SA 4.0'
---

# İşaretçiler

nil bir işaretçiden okumak açıkta kalıyor: dil bir şey koymuyor, derleyici de reddetmiyor, sonuç panik. Dört çağrının yalnızca ikisi çağıranın değerini değiştiriyor — adres alınmadan yazılan bir değer, fonksiyon parametresinde de yöntem alıcısında da yalnızca kendi kopyası üzerinde çalışıyor. Sınırlayıcı ölçüm işaretçinin de bir değer olduğunu gösteriyor: bir işaretçiyi yeniden atamak yalnızca yerel kopyayı değiştiriyor, gösterdiği yere yazmak çağıranı değiştiriyor.

Yapılar dersi bir yapının atandığında tamamen kopyalandığını, bir işlevin kendisine
geçirilen kopya üzerinde çalışırsa çağırandaki hiçbir şeyi değiştiremediğini kapattı. Bu
ders o sınırın nasıl aşıldığına bakıyor: bir değişkenin **adresini** almak, çağrılan tarafa
kopya yerine o adresi geçirmek anlamına geliyor — ve adres de kendi başına bir değer.

İşaretçi (`*T`), Go'da bir değişkenin bellekteki yerini tutan bir tiptir. `&x` yazımı
`x`in adresini üretir, `*p` yazımı bir işaretçinin gösterdiği yeri okur ya da oraya yazar.
Bu iki işleç — adres alma ve gösterileni çözme — bu dersin ölçtüğü her şeyin altında
duruyor: bir çağrının çağıranı değiştirip değiştirmemesi, hangisinin ne zaman yazıldığına
bağlı.

## Ne Zaman Adres Almak Gerekir

Bu konudaki dört tip aynı soruya dört ayrı yanıt veriyor: bir işleve geçirilen değer
değiştirildiğinde, bu değişiklik çağırana adres almadan mı ulaşıyor, yoksa adres şart mı?

```go
// adres.go — dizi, dilim ve yapida adres almanin gerekip gerekmedigi
package main

import "fmt"

type Kayit struct {
	Sayi int
}

func degistirDizi(d [3]int)       { d[0] = 99 }
func degistirDiziPtr(d *[3]int)   { d[0] = 99 }
func yazDilimeElemanla(s []int)   { s[0] = 99 }
func yazSozluge(s map[string]int) { s["a"] = 99 }
func degistirYapi(k Kayit)        { k.Sayi = 99 }
func degistirYapiPtr(k *Kayit)    { k.Sayi = 99 }

func main() {
	dizi := [3]int{1, 2, 3}
	degistirDizi(dizi)
	fmt.Println("dizi, adressiz     :", dizi)

	dizi2 := [3]int{1, 2, 3}
	degistirDiziPtr(&dizi2)
	fmt.Println("dizi, adresle      :", dizi2)

	dilim := []int{1, 2, 3}
	yazDilimeElemanla(dilim)
	fmt.Println("dilim, adressiz    :", dilim)

	sozluk := map[string]int{"a": 1}
	yazSozluge(sozluk)
	fmt.Println("sozluk, adressiz   :", sozluk["a"])

	yapi := Kayit{Sayi: 1}
	degistirYapi(yapi)
	fmt.Println("yapi, adressiz     :", yapi)

	yapi2 := Kayit{Sayi: 1}
	degistirYapiPtr(&yapi2)
	fmt.Println("yapi, adresle      :", yapi2)
}
```

```
dizi, adressiz     : [1 2 3]
dizi, adresle      : [99 2 3]
dilim, adressiz    : [99 2 3]
sozluk, adressiz   : 99
yapi, adressiz     : {1}
yapi, adresle      : {99}
```

Altı satır dört bileşik tipin aynı soruya verdiği farklı yanıtı gösteriyor. Dizi ve yapı,
adres alınmadan geçirildiğinde değişmiyor — ikisi de tamamen kopyalanan değerler, Diziler
ve Dilimler ile Yapılar derslerinde kurulan kuralın aynısı. Dilim ve sözlük ise adres
**hiç alınmadan** değişebiliyor: `yazDilimeElemanla` ile `yazSozluge` yalnızca dilimin ve
sözlüğün kendisini (başlığını) aldı, işaretçi almadı, ama ikisi de çağırandaki veriyi
değiştirdi — çünkü değişen şey başlık değil, başlığın gösterdiği paylaşılan alt dizi ya da
karma tabloydu. Dilim İşlemleri ve Sözlükler derslerinde ölçülen paylaşım, burada adres
almanın **neden gereksiz** olduğunu açıklıyor: ikisi de kendi içinde zaten bir gösterici
taşıyor.

Dizi ve yapı için adres almanın gerekmesinin nedeni tam tersi: ikisi hiçbir göstericiyi
kendi içinde taşımıyor, taşıdıkları içeriğin kendisi. İşaretçi almak, bu içeriğin
kopyalanmasını engellemenin **tek** yolu. Dört tip böylece iki gruba ayrılıyor: dizi ve
yapı **kendi içeriğini taşıyor** ve değiştirilmek istendiğinde adres istiyor; dilim ve
sözlük **bir göstericiyi taşıyor** ve değiştirilmek istendiğinde kendi kopyası bile
yeterli oluyor — tabii büyümenin kendisi (`append`, ya da sözlüğün karma tablosunun ilk
kez kurulması) ayrı bir konu, çünkü o durumda değişen şey içerik değil başlığın kendisi.

## nil İşaretçiden Okumak Açıkta Kalır

Bir işaretçi ilk değeri verilmeden tanımlandığında `nil` olur; bu, Sıfır Değerler
dersindeki bağlayıcı dağılımın **açıkta** kalan üç durumundan biriydi. `nil` işaretçinin
kendisini okumak (adresin ne olduğuna bakmak) sorun değil, ama gösterdiği yere gitmeye
çalışmak — alanına erişmek — çalışma zamanını durduruyor.

**BT14.** — Ölçüm iki ayrı soruyu birbirinden ayırıyor: işaretçinin kendisi `nil` mi
(güvenle sorulabilen bir soru), ve işaretçinin gösterdiği yerdeki alan ne (güvenle
sorulamayan bir soru). İkisi aynı satırda karıştırılmıyor.

```go
// nil.go — nil isaretciden okumak acikta kalir
package main

import "fmt"

type Kayit struct {
	Sayi int
}

func panikliyorMu(f func()) (p bool) {
	defer func() {
		if recover() != nil {
			p = true
		}
	}()
	f()
	return false
}

func main() {
	var isaretci *Kayit
	fmt.Println("isaretcinin kendisi nil mi:", isaretci == nil)

	okumaPanikledi := panikliyorMu(func() { _ = isaretci.Sayi })
	fmt.Println("isaretci.Sayi okumasi     : panik=", okumaPanikledi)
}
```

```
isaretcinin kendisi nil mi: true
isaretci.Sayi okumasi     : panik= true
```

`isaretci == nil` sorusunun kendisi güvenle yanıtlanıyor, çünkü bu, işaretçinin **kendi**
değerini okumak — bir adresi, ya da adresin yokluğunu. Ama `isaretci.Sayi` yazımı önce o
adrese **gitmeyi**, sonra oradaki `Sayi` alanını okumayı gerektiriyor; gidilecek adres
yoksa bu adım çalışma zamanında durduruyor. Sözlükler dersinde ölçülen `nil` sözlüğe yazma
panikleri ile buradaki fark: sözlükte panikleyen yalnızca **yazmaktı**, okumak tanımlıydı;
işaretçide panikleyen **okumanın kendisi**, çünkü sözlükte "aranan yer yok" tanımlı bir
sonuca (sıfır değer) bağlanabiliyordu, işaretçide "gidilecek yer yok"un tanımlı bir
karşılığı bulunmuyor.

Bu fark, iki tipin `nil` durumunu neden farklı yorumladığını gösteriyor. Sözlüğün `nil`
durumu "henüz hiçbir karma tablom yok ama arama yapabilirim" demekti — arama bulunamayan
bir anahtar için zaten tanımlı bir yanıt (sıfır değer, `false`) üretiyordu. İşaretçinin
`nil` durumu ise "gösterdiğim hiçbir yer yok" demek; burada dönülecek tanımlı bir sıfır
değer yok, çünkü işaretçinin okunması gereken şey kendi değeri değil, gösterdiği yerdeki
değer. Sıfır Değerler dersindeki üç `açıkta` durumun ortak noktası tam olarak bu: tanımlı
bir karşılık, ancak sorulan sorunun **yapısı** buna izin verdiğinde var oluyor.

## Dört Çağrı: Adres mi, Kopya mı

İşlevlere ek olarak, bir tipin **yöntemleri** de değer alıcı ya da işaretçi alıcı ile
tanımlanabilir; ikisi de nokta sözdizimiyle çağrılıyor ve çağrı yazımından hangisinin
kullanıldığı görünmüyor. Bir yöntem tanımı `func (k Kayit) Ad()` biçimindeyse alıcı
değerdir, `func (k *Kayit) Ad()` biçimindeyse alıcı işaretçidir; parantez içindeki bu tek
karakter (`*`), az önce ölçülen fonksiyon parametresi ayrımının birebir aynısını yöntemler
için de kuruyor. Ölçüm burada bilinçli olarak Yapılar dersindeki tek alanlı `Kayit` tipini
yeniden kullanıyor; farkı yaratan şeyin alanların sayısı değil, yalnızca alıcının tanımı
olduğunu görünür kılmak için.

```go
// dortcagri.go — dort cagri, ikisi cagiranin degerini degistiriyor
package main

import "fmt"

type Kayit struct {
	Sayi int
}

func kopyalaDegistir(k Kayit)   { k.Sayi = 99 }
func pointerDegistir(k *Kayit)  { k.Sayi = 99 }
func (k Kayit) DegistirV()      { k.Sayi = 99 }
func (k *Kayit) DegistirP()     { k.Sayi = 99 }

func main() {
	k1 := Kayit{Sayi: 1}
	kopyalaDegistir(k1)
	fmt.Println("deger parametreli fonksiyon   :", k1.Sayi)

	k2 := Kayit{Sayi: 1}
	pointerDegistir(&k2)
	fmt.Println("isaretci parametreli fonksiyon:", k2.Sayi)

	k3 := Kayit{Sayi: 1}
	k3.DegistirV()
	fmt.Println("deger alicili yontem          :", k3.Sayi)

	k4 := Kayit{Sayi: 1}
	k4.DegistirP()
	fmt.Println("isaretci alicili yontem       :", k4.Sayi)
}
```

```
deger parametreli fonksiyon   : 1
isaretci parametreli fonksiyon: 99
deger alicili yontem          : 1
isaretci alicili yontem       : 99
```

Dört çağrının **ikisi** çağıranın değerini değiştiriyor; ölçümün amacı bu ikiyi bulmak
değil, çağıranı değiştirmenin **çağrı yazımından değil alıcı tanımından** geldiğini
göstermek. İlk ikisi beklenen kalıbı
tekrarlıyor: değer parametresi kendi kopyasını alır, işaretçi parametresi çağırana geri
ulaşır. Son ikisi aynı kalıbı **yöntem çağrısı** biçiminde gösteriyor: `k3.DegistirV()` ile
`k4.DegistirP()` sözdizimsel olarak neredeyse özdeş — ikisi de `değişkenAdı.YöntemAdı()`
biçiminde — ama biri değer alıcı, öteki işaretçi alıcı ile tanımlı ve sonuçları farklı.
Çağrıya bakarak hangisinin çağırılacağı bilinmiyor; bunu belirleyen, yöntemin **kendi**
tanımındaki alıcı tipi.

`k4.DegistirP()` yazımının kendisi bir işaretçi içermiyor, ama derleyici `k4`ün
adresini **kendisi** alıp `(&k4).DegistirP()`e çeviriyor — çünkü `k4` adreslenebilir bir
değişken. Bu otomatik dönüşüm, işaretçi alıcılı bir yöntemi sıradan bir değer üzerinden
çağırmayı olanaklı kılıyor; adres alma adımı yazılmıyor ama gerçekleşiyor.

Bu otomatik dönüşümün bir sınırı var: yalnızca **adreslenebilir** değerler için işliyor.
Bir değişken adreslenebilir, çünkü bellekte kalıcı bir yeri var; bir işlevin dönüş
değeri gibi geçici bir değer değil. `k4.DegistirP()` çalıştı çünkü `k4` bir değişkendi;
`Kayit{Sayi: 1}.DegistirP()` gibi bir çağrı, süslü ayraçla kurulan değerin kendisi
adreslenebilir olmadığı için derlenmezdi. Aynı otomatik dönüşüm tersine de işliyor:
işaretçi alıcılı bir tipin `nil` olmayan bir işaretçisi üzerinden değer alıcılı bir
yöntem çağrıldığında, derleyici işaretçiyi kendiliğinden **çözüyor**.

Bu iki yönlü otomatik dönüşüm, bir tipin yöntemlerini hep aynı alıcı türüyle tanımlamayı
pratik bir alışkanlık hâline getiriyor: bir tipin bazı yöntemleri değer, bazıları işaretçi
alıcılıysa, çağıranın hangi çağrının çağırdığı değeri değiştirip hangisinin değiştirmediğini
yöntem yöntem ezberlemesi gerekiyor. Yukarıdaki `DegistirV`/`DegistirP` çifti bu karışıklığı
bilinçli olarak sergiliyor; gerçek bir tipte ikisinin bir arada bulunması, tam da bu ölçümün
gösterdiği belirsizliği üretir.

Alıcı seçimi yalnızca "çağıranı değiştirmek isteyip istememek" sorusuna bağlı değil. Bir
değer alıcı, yöntem her çağrıldığında o değerin **tam bir kopyasını** çıkarıyor — `Kayit`
gibi tek bir tam sayı taşıyan küçük bir tip için bu maliyet önemsiz, ama Yapılar dersindeki
gibi çok alanlı ya da başka yapıları gömen büyük bir tip için her çağrıda bütün alanları
kopyalamak anlamına geliyor. İşaretçi alıcı bu kopyalamayı, taşınan tek şey bir adres
olduğu için, yapının büyüklüğünden bağımsız kılıyor. Çağıranı değiştirmek istemeyen bir
yöntem bile, yalnızca kopyalama maliyetinden kaçınmak için işaretçi alıcı seçebiliyor.

## Sınırlayıcı Ölçüm: İşaretçi de Bir Değerdir

İşaretçinin gösterdiği yere yazmakla, işaretçinin **kendisini** yeniden atamak farklı
şeyler. Go'da her şey değerle aktarılır (Programlama Temelleri dersinde kavram olarak
kuruldu, burada tekrarlanmıyor) — işaretçi de bir istisna değil, kendisi de bir değer, ve
bir işaretçi parametresi de kopyalanıyor.

**BT15.** — İki işlev de birer tam sayı işaretçisi alıyor ve ikisi de kaynakta neredeyse
aynı görünüyor; fark tek bir satırda. Ölçüm bu iki satırı bilinçli olarak yan yana koyuyor
ki fark yalnızca yazıdan okunabilsin, çalıştırmaya gerek kalmasın — sonra ikisi ayrı ayrı
koşturulup bu okumanın doğru olduğu doğrulanıyor.

```go
// deger.go — isaretcinin kendisi de kopyalanan bir degerdir
package main

import "fmt"

func yenidenAta(p *int) {
	yeni := 99
	p = &yeni // yalnizca yerel kopyayi degistirir
}

func degerYaz(p *int) {
	*p = 99 // isaretcinin gosterdigi yere yazar
}

func main() {
	x := 1
	yenidenAta(&x)
	fmt.Println("isaretciyi yeniden atamak      :", x)

	y := 1
	degerYaz(&y)
	fmt.Println("isaretcinin gosterdigine yazmak:", y)
}
```

```
isaretciyi yeniden atamak      : 1
isaretcinin gosterdigine yazmak: 99
```

`yenidenAta` çağrıldığında `x`in adresi `p`ye kopyalanıyor; işlev içinde `p = &yeni`
yazıldığında değişen yalnızca `p`nin **kendi kopyası** — artık başka bir adresi gösteriyor,
ama çağırandaki `x` hiç etkilenmiyor, çünkü `x`in kendisi hiçbir zaman `yeni`nin adresini
almadı. `degerYaz` ise `p`nin gösterdiği adrese `*p = 99` ile doğrudan yazıyor; bu adres
hâlâ `x`inki ile aynı olduğu için (bu çağrıda `y`), yazma çağırana ulaşıyor.

Bu fark, dört çağrı ölçümündeki "işaretçi alınca değişir" kuralının **neden** doğru
olduğunu açıklıyor. İşaretçi parametreli bir fonksiyonun çağıranı değiştirmesi, işaretçinin
sihirli bir "referans" olmasından değil, işaretçinin **taşıdığı adresin** kopyalanıp
kopyalanan adrese yazılmasından geliyor. Adres kopyalansa bile gösterdiği yer aynı kaldığı
sürece, o yere yapılan yazma her kopyadan görünür — tıpkı dilimin başlığı kopyalandığında
alt dizinin paylaşılmaya devam etmesi gibi. İşaretçi, kopyalandığında paylaşımı sürdüren bir
değerdir; farkı, dilimin aksine, gösterdiği şeyin bir alt dizi değil **tek bir değer**
olmasıdır.

Bu ders boyunca ölçülen dört tip — dizi, dilim, sözlük, yapı — ve işaretçinin kendisi,
aynı tek kuralın beş farklı görünümüdür: Go'da atama, parametre geçişi ve dönüş hep aynı
işi yapar, bir değeri kopyalar. Görünürdeki fark, kopyalanan değerin **ne taşıdığından**
geliyor. Dizi ve yapı içeriği taşıyor, kopya içeriği de kopyalıyor. Dilim ve sözlük bir
göstericiyi taşıyor, kopya göstericiyi de kopyalıyor ama gösterilen yeri değil — bu yüzden
paylaşım sürüyor. İşaretçi de aynı kalıba uyuyor: taşıdığı tek şey bir adres, kopya adresi
kopyalıyor, adresin gösterdiği yeri değil. "Referansla aktarım" hiçbir zaman ayrı bir
düzenek olarak var olmadı; var olan, bazı değerlerin bir gösterici taşıması ve bazılarının
taşımamasıydı.

## Özet

- Dizi ve yapı adres alınmadan geçirildiğinde değişmez, çünkü ikisi de tamamen kopyalanır;
  dilim adres almadan da değişebilir, çünkü kendi içinde zaten bir gösterici taşır.
- `nil` bir işaretçinin kendisini okumak güvenlidir; gösterdiği yere gitmeye çalışmak
  (bir alana erişmek) çalışma zamanını durdurur — Sıfır Değerler dersindeki açıkta
  sınıfının bir örneği.
- Dört çağrının (değer parametresi, işaretçi parametresi, değer alıcılı yöntem, işaretçi
  alıcılı yöntem) yalnızca ikisi çağıranın değerini değiştirir; ayrım nokta sözdiziminden
  değil, alıcının tanımından okunur.
- İşaretçi kendisi de bir değerdir ve kopyalanır: bir işaretçiyi yeniden atamak yalnızca
  yerel kopyayı değiştirir, gösterdiği yere yazmak (`*p = ...`) çağırana ulaşır.

## Sonraki Adım

Bu derste ölçülen tipler — dizi, dilim, yapı, işaretçi — hep sayı ya da adlandırılmış alan
taşıyordu; hiçbiri kendi içinde metin taşımıyordu. Metin bunlardan farklı bir bileşik tip:
bir dizgi, dışarıdan bakıldığında tek parça görünen ama içeride bir bayt dizisi olan bir
değer. Dizgi de değiştirilemez olduğu için burada ölçülen kopya/paylaşım ayrımının dışında
kalıyor, ama kendi yazılmayan sorusunu taşıyor. Sıradaki ders şu soruyu soruyor: bir
dizginin uzunluğu neyi sayıyor, bir dizgiyi indekslemek neyi döndürüyor, ve bu ikisi
"karakter" sözcüğünün sezgisiyle ne zaman ayrışıyor?
