Golang Slice’lar Nasıl Çalışır?

Ömer Burak Demirpolat
3 min readApr 4, 2020

Slice’lar nasıl çalışıyor, bellekte nasıl yerleşiyor, bir Slice’a ekleme, çıkarma işlemlerinde performans için neler yapılmış, neler düşünülmüş merak ediyordum. Bugün yazıya dökmek istedim, buyrunuz.

Go dilinde Array ve Slice yapılarının en temel farkı, Slice büyüyebilir, Array ise sabit boyuta sahiptir. Aynı zamanda bu iki yapının ortak özellikleride var.

  1. ptr (Pointer) Array veya Slice’ın ilk elemanının bellekteki adresini tutar.
  2. len (Length) anlaşıldığı üzere Array veya Slice’ın Integer tipinden boyutunu içeriyor.
  3. cap (Capacity) Array veya Slice’ın kapatsitesini tutar.

Bir Slice oluşturup incelemeye başlayalım.

items := [4]int{1,2,3,4}

Peki yukarıdaki kod doğrultusunda bilgisayarımızda neler oldu?

Bilgisayarımızın belleğinde “items” değişkeni için bir yer ayrıldı fakat bu ayrılan kısım bir veri içermiyor, içerdiği şeyler yukarıdaki 3 maddede görüldüğü gibi.

Şu an belleğimizde bizim tanımladığımız 5 adet veri bulunuyor. “items” değişkeni, bu değişken yukarıdaki 3 maddede yer alan verileri tutuyor ve Slice içerisinde tanımladığımız “1,2,3,4” değerleri. Bunu aşağıdaki kodu yazarak test edebiliriz.

var items []int
fmt.Println(unsafe.Pointer(&items))
//0xc00000c080
items = []int{1,2,3,4}
fmt.Println(unsafe.Pointer(&items))
//0xc00000c080
fmt.Println(unsafe.Pointer(&items[0]))
//0xc0000180c0
fmt.Println(unsafe.Pointer(&items[1]))
//0xc0000180c8
fmt.Println(unsafe.Pointer(&items[2]))
//0xc0000180d0
fmt.Println(unsafe.Pointer(&items[3]))
//0xc0000180d8

Peki “items” bir veri içermiyor ise içerisinde yer alan elemanlara nasıl ulaşıyor nasıl bağlantı kuruyor? Kısaca aşağıdaki kod nasıl çalışıyor?

fmt.Println(items[0])
//1

Yukarıda belirtildiği üzere “items” değişkeni içerisinde Pointer olarak Slice’ın ilk elemanını barındırıyor. Slice içerisindeki “1,2,3,4” değişkenleri bellekte ard arda gelecek şekilde numaralandırılmış, yani sıralı durumda. İlk elemana Pointer sayesinde ulaşan Slice yine barındırdığı “len (Length)” sayesinde diğer elemanlara da ulaşabilmektedir.

Yani Go programımız Slice’ımızın içindeki Pointer sayesinde 0. elemanı bellekten istiyor, sonrasında 1. elemana ulaşmak bellekte yer alan sıradaki numarayı isteyecektir. Slice içindeki verilerin Stack’de sıralı olarak yer tahsis etmesinin nedeni budur.

Peki bir ekleme işleminde neler oluyor inceleyelim.

items := []int{1,2,3,4}
fmt.Println(unsafe.Pointer(&items))
//0xc00009e000
firstHeader := (*reflect.SliceHeader)(unsafe.Pointer(&items))
fmt.Println(firstHeader)
//&{824634376192 4 4}

items = append(items,5)
fmt.Println(unsafe.Pointer(&items))
//0xc00009e000

secondHeader := (*reflect.SliceHeader)(unsafe.Pointer(&items))
fmt.Println(secondHeader)
//&{824634392576 5 8}

Yukarıdaki kodda görüldüğü üzere, 4 elemanlı bir Slice oluşturuldu, oluşturulan Slice’ın Pointer’ı 0xc00009e000 olarak görüntüleniyor.

Slice’ın içerdiği 0. elemanın Pointer’ı ise 824634376192 olarak görüntüleniyor.

Şimdi built-in fonksiyonlardan birisi olan “append” ile bir eleman ekliyoruz.

Bu eklemeyi yaptığımda “items” içerisindeki veriler kopyalanıyor, yeni eklenen eleman ile birleştiriliyor ve bu birleştirilmiş data ile yeni bir değişken oluşturulup Stack’e push ediliyor, yani ekleniyor.

Bu ekleme sonucunda “items” adlı değişkenimizin Pointer’ı halen 0xc00009e000 olarak kalmış.

Fakat “items” adlı değişkenin 0. elemanının Pointer’ı daha öncesinde 824634376192 olarak görüntülenirken şimdi ise 824634392576 olarak değişmiş gözüküyor.

Şimdi “append” fonksiyonuyla yeni bir eleman eklediğimde neden var olan Slice’a eklenmiyor, neden yeni bir değişken olarak Stack’e eklendi?

Bunun sebebi biraz konuyu uzatabilir ama asıl olay burada gibi.

Stack’de verilerin sırayla yer aldığını bildirmiştik. Biz Stack’de 4 adet int tipinde değer içeren yer tahsis ediliyor, ayrılıyor. Programımız devam ettiğinde ise tanımladığımız herhangi bir başka değişken araya girip sıradaki Stack numarasını kendine tahsis edebilir. Hatırlarsanız yazının ilk kısımlarında Slice veya Array içindeki verilerin Stack’de ard arda alanları tahsis ettiğini, Slice’ın bu şekilde içerisindeki verilere ulaşabildiğini belirtmiştik. Eğer araya giren başka bir değişken Stack’de yer tahsis ederse Slice yeni eklenecek verilere ulaşamayacaktır.

Anlaşılır olması için “items” adlı Slice’ın bellekteki verileri şu şekilde tahsis ettiğini varsayalım.

items[0] ---> Bellek adresi x0
items[1] ---> Bellek adresi x1
items[2] ---> Bellek adresi x2
items[3] ---> Bellek adresi x3
arayaGirenDegisken := 0 ---> Bellek adresi x4
items = append(items, 4)
items[4] ---> Bellek adresi x5

Belirtildiği üzere Slice’ın elemanlara ulaşabilmesi için elemanların ardışık olarak bellekte yer tahsis etmesi gerekiyor, fakat yukarıda görüntülendiği üzere bu şekilde bir tanımlama yapılsaydı “items” adlı Slice’ın 4. elemanına ulaşamayacaktık. Bu nedenle ekleme sırasında veriler kopyalanıp yeni bir değişkene aktarılıyor.

Şimdilik bu kadar, Slice’ları parçalamak, çıkarma işlemi yapmak gibi konulara daha sonra devam edeceğim.

Kaynaklar

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

No responses yet

Write a response