---
title: 'Diziler ve Dilimler'
source: 'https://academia.sh/tr/kurslar/go-temelleri/diziler-ve-dilimler'
course: 'Go Temelleri'
language: tr
updated: '2026-08-23T14:24:59+00:00'
license: 'CC BY-SA 4.0'
---

# Diziler ve Dilimler

Aynı atama satırı dizide kopya, dilimde paylaşım üretiyor: dizinin uzunluğu tipin parçasıdır ve atandığında bütün öğeler kopyalanır, dilimin atanması ise yalnızca başlığı kopyalar ve alt diziyi kaynakla paylaşır. Sınırlayıcı ölçüm dilimin de bir değer olduğunu gösteriyor: paylaşım, append çağrısı kapasiteyi aştığında kesiliyor, çünkü kopyalanan başlık artık ayrı bir alt diziyi gösteriyor.

Değişken Sayıda Argüman dersi, hiç argüman verilmeyen bir parametrenin `nil` bir dilim
olduğunu ölçtü — sıfır değerin en görünür sonuçlarından biriydi bu. Bu ders aynı tipin
kendisine döner: dilim, sözlük ve yapı yazılmayanı nasıl karşılıyor, ve daha önce hiç
sorulmamış bir soru — bir değeri bir değişkene atadığında kopya mı oluşuyor, paylaşım mı?

Veri Yapıları kursu diziyi ve listeyi soyut veri tipi olarak kurdu ve gerçekleştirim
seçeneklerini karmaşıklıkla karşılaştırdı; o karşılaştırma burada tekrarlanmıyor. Burada
ölçülen Go'nun kendi iki tipinin — dizi ve dilim — atama sırasında **gözlemlenebilir**
davranışıdır: aynı `=` işareti, sağdaki değerin tipine göre iki ayrı şey yapıyor.

Dizi, dilim, sözlük ve yapı bu kursta **bileşik tip** ortak adıyla anılıyor, çünkü dördü de
başka tiplerden kurulu: bir dizi `int`lerden, bir yapı adlandırılmış alanlardan. Bileşik
tipler arasındaki fark, atama sırasında ne kadarının kopyalandığında yatıyor — ve bu ders o
farkın en temel iki üyesiyle, dizi ile dilimle, başlıyor.

## Dizi: Sabit Uzunluk ve Kopyalanan Atama

Go'da bir dizinin uzunluğu tipin parçasıdır; `[4]int` ile `[3]int` iki ayrı tiptir, tıpkı
`int` ile `string`in iki ayrı tip olması gibi. Bu, "dört elemanlık bir dizi" gibi gevşek
bir tanım değil, derleyicinin sözdiziminden okuduğu kesin bir bilgidir.

**BT1.** — Uzunluğu farklı iki dizi tipi arasında doğrudan atama yapan bir kaynağın
derlenmediği burada bir derleyici çağrısıyla koşturulmuyor; ölçüm bunun yerine `%T`
biçimlendirme fiiliyle aynı bilgiyi dolaylı gösteriyor — basılan tip adının kendisi
uzunluğu içeriyor.

```go
// bilesik.go — dizi atamasi kopyalar, tip adi uzunlugu tasir
package main

import "fmt"

func main() {
	kaynakDizi := [4]int{10, 20, 30, 40}
	kopyaDizi := kaynakDizi
	kopyaDizi[0] = 99
	fmt.Println("tip           :", fmt.Sprintf("%T", kaynakDizi))
	fmt.Println("kaynakDizi    :", kaynakDizi)
	fmt.Println("kopyaDizi     :", kopyaDizi)
}
```

```
tip           : [4]int
kaynakDizi    : [10 20 30 40]
kopyaDizi     : [99 20 30 40]
```

Aynı kaynakta `kopyaDizi := kaynakDizi` satırının hemen üstüne `var kopyaDizi [3]int`
yazılsaydı derleme burada dururdu; `[3]int` ile `[4]int` birbirinin yerine geçmeyen iki tip
olduğu için atama reddedilirdi. Bu ders bunu ayrı bir derleyici çağrısıyla göstermiyor,
çünkü Go'nun Tasarım Amaçları dersi örtük tip dönüşümünün reddini zaten ölçmüştü; buradaki
fark yalnızca uzunluğun da tip kimliğinin bir parçası olduğudur.

Basılan tip `[4]int`tir, yalnızca `int` değil — uzunluk tip adının içinde durur.
`kopyaDizi := kaynakDizi` satırından sonra ilk öğeyi değiştirmek yalnızca `kopyaDizi`yi
etkiliyor; `kaynakDizi` değişmeden kalıyor. Dizi atandığında dört tam sayının **hepsi** yeni bir
değişkene kopyalanıyor — sözdizimi tek bir eşittir işareti olsa da, altında dört ayrı
kopyalama işlemi var.

Bu kopyalama yalnızca değişkenden değişkene atamada değil, bir fonksiyona argüman
geçirilirken de işliyor; parametreler de birer atamadır. Uzunluğu tipin parçası olmasının
bir başka sonucu da buradan çıkıyor: iki dizi, öğe tipleri karşılaştırılabilir olduğu
sürece, `==` ile doğrudan karşılaştırılabiliyor — çünkü karşılaştırılan şey bir işaretçi
değil, dizinin taşıdığı **içeriğin kendisi**. Aynı özellik dizinin bir sözlük anahtarı
olarak kullanılabilmesini de açıklıyor; Sözlükler dersinde görüleceği gibi bir sözlüğün
anahtar tipi karşılaştırılabilir olmak zorundadır, ve dizi bu koşulu doğal olarak sağlar.
Aynı kural aşağı doğru da işliyor: bir alanı dilim olan bir yapı da karşılaştırılamaz hâle
gelir, çünkü yapının karşılaştırılabilirliği alanlarının karşılaştırılabilirliğine bağlıdır
— bu, Yapılar dersinde tekrar karşımıza çıkacak.

## Dilim: Uzunluk, Kapasite ve Paylaşılan Alt Dizi

Dilim bir dizi değildir; dilimin kendisi üç parçadan oluşan küçük bir yapıdır: alt diziye
giden bir işaretçi, geçerli uzunluk ve kapasite. `[]int{10, 20, 30, 40}` yazımı bu üçünü
bir kerede kurar — arkasında dört öğelik bir alt dizi üretir ve dilim o alt diziyi gösterir.

```go
// paylasim.go — dilim atamasi basligi kopyalar, alt dizi paylasilir
package main

import "fmt"

func main() {
	kaynakDilim := []int{10, 20, 30, 40}
	payDilim := kaynakDilim
	payDilim[0] = 99
	fmt.Println("kaynakDilim   :", kaynakDilim)
	fmt.Println("payDilim      :", payDilim)
	fmt.Println("uzunluk       :", len(kaynakDilim), " kapasite:", cap(kaynakDilim))
}
```

```
kaynakDilim   : [99 20 30 40]
payDilim      : [99 20 30 40]
uzunluk       : 4  kapasite: 4
```

Aynı satır — `payDilim := kaynakDilim` — dizideki gibi bir kopya üretmiyor.
`payDilim[0] = 99` yazıldığında `kaynakDilim` de değişiyor, çünkü ikisi aynı alt diziyi gösteriyor; atanan
şey alt dizinin kendisi değil, ona giden işaretçiyi taşıyan küçük başlıktır. `uzunluk` ve
`kapasite` burada eşit çıkıyor çünkü dilim değişmezi tam olarak dört öğeyle kuruldu; bir
dilim dilimlemeyle türetildiğinde bu ikisi ayrışabilir — bu ayrım Dilim İşlemleri dersinin
konusu.

Başlığın küçük ve sabit boyutlu olmasının doğrudan bir sonucu var: bir dilimi kopyalamanın
maliyeti, alt dizinin kaç öğe tuttuğundan bağımsızdır. On öğelik bir dilimi atamak da on
milyon öğelik bir dilimi atamak da aynı üç alanı kopyalar; kopyalanan hiçbir zaman öğelerin
kendisi olmuyor. Diziyi atamanın maliyeti ise uzunlukla birlikte büyür, çünkü her öğe tek
tek kopyalanıyor.

Bu ayrım iki tipin nerede kullanılacağını da belirliyor. Uzunluğu koddan önce bilinen ve
hiç değişmeyecek küçük veriler — dört baytlık bir IPv4 adresi, sabit uzunluklu bir
sağlama toplamı — dizi olarak tanımlanır; boyutu tip imzasına yazmak, o boyutun asla
değişmeyeceğini derleyiciye söylemenin bir yoludur. Uzunluğu koşum sırasında değişecek her
şey — okunan satır sayısı, biriktirilen sonuç kümesi — dilim olarak tutulur, çünkü dilimin
uzunluğu tipin değil değerin bir alanıdır.

Dilimin `==` ile karşılaştırılamaması da aynı köke bağlanıyor. Bir dilim karşılaştırması
"içerik aynı mı" sorusuna yanıt vermeye kalksaydı derleyicinin her karşılaştırmada alt
diziyi baştan sona gezmesi gerekirdi; dil bunun yerine dilimi yalnızca `nil` ile
karşılaştırmaya izin veriyor, iki dilimi birbiriyle değil. Aynı `==` işleci dizide
derleniyor, dilimde derlenmiyor — çünkü dizi taşıdığı içeriğin kendisidir, dilim ise ona
giden bir göstericidir.

**BT2.** — Bu ayrım kaynak bir derleyici çağrısıyla koşturulur; basılan iki satır
yalnızca "derlendi" ya da "derlenmedi" sınıfını taşır, derleyicinin hata metni okunmaz.

```go
// karsilastirma.go — dizi == ile karsilastirilir, dilim karsilastirilamaz
package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
)

func gecerMi(kaynak string) bool {
	dizin, err := os.MkdirTemp("", "sinama")
	if err != nil {
		return false
	}
	defer os.RemoveAll(dizin)
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module sinama\n\ngo 1.24\n"), 0o644)
	os.WriteFile(filepath.Join(dizin, "kod.go"), []byte(kaynak), 0o644)
	cmd := exec.Command("go", "build", "./...")
	cmd.Dir = dizin
	cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
	return cmd.Run() == nil
}

func main() {
	diziEsit := `package sinama
	func F() bool {
		a := [3]int{1, 2, 3}
		b := [3]int{1, 2, 3}
		return a == b
	}`
	dilimEsit := `package sinama
	func F() bool {
		a := []int{1, 2, 3}
		b := []int{1, 2, 3}
		return a == b
	}`
	fmt.Println("iki dizi == ile karsilastirilir :", gecerMi(diziEsit))
	fmt.Println("iki dilim == ile karsilastirilir:", gecerMi(dilimEsit))
}
```

```
iki dizi == ile karsilastirilir : true
iki dilim == ile karsilastirilir: false
```

## Fonksiyona Geçerken Aynı Kural İşler

Parametre geçişi de bir atamadır ve yukarıdaki kural burada da aynen işliyor: dizi
parametresi kendi kopyasını alır, dilim parametresi aynı başlığı — dolayısıyla aynı alt
diziyi — paylaşır. Bu, iki değişken arasındaki atamayla fonksiyon çağrısı arasında Go'nun
hiçbir ayrım yapmadığının bir kanıtı: parametre bağlama da `=` işaretiyle aynı kuralı
izliyor.

```go
// gecis.go — fonksiyona gecis de bir atamadir
package main

import "fmt"

func diziDegistir(d [4]int) {
	d[0] = 999
}

func dilimElemanDegistir(d []int) {
	d[0] = 999
}

func dilimYenidenAta(d []int) {
	d = append(d, 777, 777, 777, 777, 777) // kapasiteyi asar, yeni alt diziye gecer
}

func main() {
	dizi := [4]int{1, 2, 3, 4}
	diziDegistir(dizi)
	fmt.Println("dizi fonksiyona gecti  :", dizi)

	dilim := []int{1, 2, 3, 4}
	dilimElemanDegistir(dilim)
	fmt.Println("dilim eleman yazma     :", dilim)

	dilim2 := []int{1, 2, 3, 4}
	dilimYenidenAta(dilim2)
	fmt.Println("dilim yeniden atama    :", dilim2)
}
```

```
dizi fonksiyona gecti  : [1 2 3 4]
dilim eleman yazma     : [999 2 3 4]
dilim yeniden atama    : [1 2 3 4]
```

İlk satır beklendiği gibi: `diziDegistir` kendi kopyası üzerinde çalışıyor, çağıranın
dizisi değişmiyor. İkinci satır paylaşımı doğruluyor: `dilimElemanDegistir` parametresinin
gösterdiği alt diziye yazıyor ve bu yazma çağırana geri yansıyor. Üçüncü satır ise ince bir
noktayı gösteriyor: `dilimYenidenAta` parametreyi `append` ile büyütüp **yeniden atadığında**
yalnızca kendi yerel kopyasının başlığı değişiyor; çağırandaki `dilim2`nin başlığı hâlâ eski
alt diziyi gösteriyor ve fonksiyon döndüğünde o değişiklik kayboluyor. Fonksiyona bir dilim
geçirmek alt diziyi paylaştırır ama başlığı paylaştırmaz — bu, Sınırlayıcı Ölçüm
bölümündeki header ayrılmasının fonksiyon çağrısı bağlamındaki karşılığıdır.

Standart kitaplığın kendi `append` işlevi de bu yüzden bir sonuç döndürür ve çağıranın onu
geri atamasını bekler; işlev parametre olarak aldığı dilimi büyütüp kendi içinde tutsaydı
bile bu, çağırandaki değişkeni etkilemezdi. `dilimYenidenAta` işlevinin üçüncü satırdaki
davranışı, `append`in neden `s = append(s, x)` biçiminde çağrıldığının doğrudan kanıtıdır:
sonucu geri atamayı unutan kod, kapasite aşıldığında sessizce eski dilimle kalır.

## Sınırlayıcı Ölçüm: Dilim Başlığı Ne Zaman Ayrılır

Dilimin paylaştığı şey alt dizidir, başlığın kendisi değil. Başlık da bir değerdir ve
atandığında kopyalanır — üç alanı (işaretçi, uzunluk, kapasite) birlikte. Bu fark, iki
değişken aynı alt diziyi göstermeyi bıraktığı anda görünür oluyor.

**BT3.** — Buradaki kapasite `make` ile açıkça istenmiyor; `[]int{1, 2, 3}` bir dilim
değişmezidir ve derleyici kapasiteyi üç öğeye eşitliyor. Ölçüm bu kapasiteyi sabit
varsaymıyor, koşumdan `cap` ile okuyor.

```go
// baslik.go — dilim basligi ne zaman kopyadan ayrilir
package main

import "fmt"

func main() {
	temel := []int{1, 2, 3}
	pay := temel
	pay[0] = 100
	fmt.Println("eleman yazmadan sonra   temel:", temel, " pay:", pay)

	oncekiKapasite := cap(temel)
	temel = append(temel, 4)
	pay[1] = 200
	fmt.Println("append sonrasi          temel:", temel, " pay:", pay)
	fmt.Println("kapasite (bu kosumda) onceki=", oncekiKapasite, " sonraki=", cap(temel))
}
```

```
eleman yazmadan sonra   temel: [100 2 3]  pay: [100 2 3]
append sonrasi          temel: [100 2 3 4]  pay: [100 200 3]
kapasite (bu kosumda) onceki= 3  sonraki= 6
```

İlk satırda `pay[0] = 100` hem `temel`i hem `pay`i değiştiriyor — ikisi hâlâ aynı alt
diziyi gösteriyor, tıpkı önceki bölümdeki gibi. Sonra `temel = append(temel, 4)` çağrısı
kapasiteyi aşıyor (üç öğelik alt dizide dördüncü öğeye yer yok); bu durumda dilin çalışma
zamanı **yeni** bir alt dizi ayırıyor, eski verileri oraya kopyalıyor ve `temel`in başlığını
o yeni alt diziyi gösterecek şekilde güncelliyor. `pay`in başlığı bu güncellemeden habersiz
kalıyor — hâlâ eski alt diziyi gösteriyor. Bu yüzden `pay[1] = 200` yalnızca `pay`i
değiştiriyor; `temel` üzerinde hiçbir iz bırakmıyor.

Bu geçişin öngörülemezliği asıl riski taşıyor. Kaynak koda bakan biri `pay[1] = 200`
satırının `temel`i etkileyip etkilemeyeceğini, yalnızca satırın kendisinden okuyarak
söyleyemez — cevap, o satıra gelene kadar `pay` ile `temel`in kaç kez ortak bir `append`
sınırını aştığına bağlıdır. İki dilim aynı alt diziyi paylaşırken biri büyütülüp diğeri
büyütülmediğinde, ikisi arasındaki paylaşım metinde hiçbir iz bırakmadan sona eriyor.

**BT4.** — Basılan iki kapasite sayısı (3 ve 6) bu koşumun gözlemidir, dilin sözü değildir.
Go belirtimi `append`in kapasiteyi aştığında yeni bir alt dizi ayıracağını garanti eder ama
yeni kapasitenin **ne kadar** büyüyeceğini belirtmez; büyüme oranı bir gerçekleştirim
ayrıntısıdır ve derleyici sürümleri arasında değişebilir. Kod bu satırı bir sabit gibi
okumamalı — yalnızca "kapasite aşıldı, yeni bir alt dizi geldi" bilgisini okumalı.

Kopyalanan şey burada iyice açık: `temel` ve `pay` hiçbir zaman aynı değişken olmadı,
ikisi de baştan beri kendi başlığına sahipti. Paylaşım, iki başlığın **aynı alt diziyi**
göstermesinden geliyordu; `append` alt diziyi değiştirdiğinde paylaşım bitiyor. Dilimin
kendisi bir değerdir — tıpkı dizinin bir değer olması gibi — ama taşıdığı değer alt dizinin
verisi değil, ona giden bir göstericidir.

Bunun pratikteki karşılığı şu: bir fonksiyon aldığı dilime `append` ile öğe ekliyorsa ve bu
büyümenin çağırana da yansımasını istiyorsa, büyümüş dilimi **geri döndürmek** zorundadır —
tıpkı `append`in kendi imzasının yaptığı gibi. Parametre olarak gelen dilim değişkeninin
kendisini büyütmek, yalnızca o yerel değişkeni günceller; çağırandaki başlık bundan
etkilenmez. Dizi ile dilim arasındaki fark böylece tek bir cümleye iniyor: dizi bir
değerdir ve tamamıyla kopyalanır, dilim de bir değerdir ama taşıdığı şey paylaşılan bir alt
diziye giden küçük bir başlıktır — ikisi de "referansla aktarım" değil, farklı içerikli bir
değerle aktarımdır. Bu ayrımın işaretçilerle ne ilgisi olduğu — bir değişkenin adresini
almanın ne zaman gerektiği, ne zaman gereksiz olduğu — İşaretçiler dersinde ayrıca ölçülüyor;
burada kurulan yalnızca dilimin kendi başlığının bir kopyalama biriminden ibaret olduğu.

## Özet

- Dizinin uzunluğu tipin parçasıdır (`[4]int`); dizi atandığında bütün öğeler kopyalanır ve
  iki değişken birbirinden bağımsız hâle gelir.
- Dilim üç alanlı bir başlıktır: alt diziye giden işaretçi, uzunluk, kapasite. Dilim
  atandığında bu başlık kopyalanır ama alt dizi kopyalanmaz; iki değişken aynı alt diziyi
  paylaşır.
- Paylaşılan alt diziye yazılan bir değişiklik her iki değişkenden de görünür, çünkü ikisi
  de aynı belleği gösterir.
- `append` çağrısı kapasiteyi aştığında yeni bir alt dizi ayrılır ve yalnızca çağrının
  sonucunu tutan değişken o yeni alt diziye geçer; paylaşım burada kesilir.
- Kapasitenin büyüme oranı (3'ten 6'ya gibi) bir gerçekleştirim ayrıntısıdır; dilin sözü
  yalnızca "kapasite aşılınca yeni bir alt dizi ayrılır" ifadesidir.

## Sonraki Adım

Bu derste paylaşım, yalnızca iki değişken aynı dilimi doğrudan taşıdığında görüldü —
`payDilim := kaynakDilim` gibi bir atamayla, ya da bir fonksiyona parametre olarak
geçirilerek. Ama bir dilim başka bir dilimden **dilimlenerek** türetildiğinde de aynı alt
dizi paylaşılır — ve bu durumda paylaşımın ne zaman süreceği, ne zaman keseceği yazılan
koda değil, o türetilen dilimin kapasitesine bağlıdır. Sıradaki ders bu bağımlılığı ölçer:
aynı `append` çağrısı, aynı kaynak metinle, iki ayrı kapasitede iki ayrı sonuç verir.
