Forum: D Programlama Dili RSS
diziler belleğe nasıl yerleştirilir ?
canalpay (Moderatör) #1
Kullanıcı başlığı: Can Alpay Çiftçi
Üye Tem 2009 tarihinden beri · 1133 mesaj · Konum: İzmir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Konu adı: diziler belleğe nasıl yerleştirilir ?
Benim düşüncelerim:

Benim bildiğim kadarıyla diziler belleğe sırasıyla yerleştirilir ve harcadığı alan ise toplam eleman(mı yoksa kapasite mi bilmiyorum ancak statik dizide farketmiyor.) çarpı eleman sayısı. Programda denedim ve statik dizilerde doğru sonuç verdi.

Ancak bunun aynısı dinamik diziler için doğru olamaz. Diziye ne kadar eleman ekleneceği belli olmadığından ne kadar yer ayrılacağıda belli olamaz. Örneğin 4 elemanlı a adlı bir int dizisi belleğe aktarıldığında sırasıyla belleğe yerleştirilsin. Ardından bir int yerleştirilsin. Ardından a dizisine eleman eklendiğinde nereye eklenecek ? Diziler liste olmadığı için bir sonraki elemanın nereye yerleşeceğini takip edemez ? O zaman tek bir çözüm kalıyor. Dizinin yerleştirilen başka boş bir alana yerleştirilecek ve ardından yeni eleman yerleştirilecek. Ancak bunu yazdığım bir programda en sonunda gözlemledim. Ancak anlamadığım bazı şeyler var:

import std.stdio;
 
import std.array;
 
void main()
 
{
 
    int[] a;
 
    int[] c = new int[5];
 
    int b;
 
    writeln("a kapasite: ",a.capacity);
// elemanı artsada hep aynı adresten başlıyor
   // ve b dizisi ile aralarında hep aynı sayıda bayt boşluğu var.
  // a dizisinin adresi neyi gösteriyor. Neyi depoluyor?
    writeln("b kapasite: ","yok normal birlik int");
 
    writeln("c kapasite: ",c.capacity);
 
    writefln("%d",&a);
 
    writefln("%d",&b);
 
    writefln("%d",&c);
 
    a~=0;
 
    writeln("ilk a : ", &a );
 
    
 
    foreach(i;0..10){
 
    writeln("---\n",i,"a kapasite: ",a.capacity);
 
    writeln("b kapasite: ","yok normal birlik int");
 
    writeln("c kapasite: ",c.capacity);
 
    writefln("%d",&a);
 
    writefln("%d",&b);
 
    writefln("%d",&c);
 
    a~=i;
 
    }
 
    writeln("a[8] : ", &a[8],"\na[0] : ",&a[0]);
 
}

Ayrıca capacity ne olduğunu şöyle düşündüm: capacity ilk adresten sonra kaç bit alanı daha o diziye verileceğini söylüyor. örneğin kapasitesi 4 olan ancak hiç elemanı olmayan bir  a dizisi için adres şu şekilde olabilir:

00000000 // 1.eleman
00000004 //2.eleman
00000008 //3.eleman
0000000c //4.eleman başladığı yer.
0000000f//4.elemanın bittiği yer.
00000010 // başka bir elemanın başladığı yer.

Ancak bu adresler alındığı halde bu adreslere erişirken hata veriyor. Çünkü işleri boş ?


Not: dizilerin nereye yerleşeceğini ve hangi sırada yerleşeceğini derleyici mi karar veriyor yoksa işletim sistemi mi ? İşletim sistemi karar veriyorsa eğer sırasıyla yerleştirmezse(sanırım popüler olanlar sırasıyla yerleştiriyor.) nasıl sırayı takip edebiliyor ? (Örne dizi[2] adresinin yerini işletim sistemi nasıl bilebiliyor.) Derleyici ayrıca nasıl yer edinmek istediğini belirtiyor ?
acehreli (Moderatör) #2
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
canalpay on 2010-07-22, 05:31:
Benim bildiğim kadarıyla diziler belleğe sırasıyla yerleştirilir ve harcadığı alan ise toplam eleman(mı yoksa kapasite mi bilmiyorum ancak statik dizide farketmiyor.)

Evet, sabit uzunluklu dizilerde kapasite ile uzunluk aynıdır. Hatta ikisi için de yer ayrılmasına gerek yoktur; çünkü bu bilgi derleme zamanında bellidir.

çarpı eleman sayısı.

Tabii ki "eleman çarpı eleman sayısı" demek istemedin. Eleman sayısı çarpı eleman uzunluğu (.sizeof) olacak.

Ancak bunun aynısı dinamik diziler için doğru olamaz. Diziye ne kadar eleman ekleneceği belli olmadığından ne kadar yer ayrılacağıda belli olamaz. Örneğin 4 elemanlı a adlı bir int dizisi belleğe aktarıldığında sırasıyla belleğe yerleştirilsin.

Yani 4 elemanı var; ama daha fazla eleman için kapasitesi olabilir. Sonrasını düşünüyor olabilir.

Ardından bir int yerleştirilsin. Ardından a dizisine eleman eklendiğinde nereye eklenecek ? Diziler liste olmadığı için bir sonraki elemanın nereye yerleşeceğini takip edemez ? O zaman tek bir çözüm kalıyor. Dizinin yerleştirilen başka boş bir alana yerleştirilecek ve ardından yeni eleman yerleştirilecek.

Evet, dizinin elemanları bellekte her zaman için yan yana dururlar.

a) Kapasite dolana kadar, yeni eklenen elemanlar için yer var demektir.

b) Eğer kapasite dolmuşsa bile, yine de komşu belleğin kullanılmadığı durumlarda şanslıyız (Hani yakın zaman önce dizi kapasiteleriyle ilgili bir dmd hatası bulmuştuk; orada da bu anlatıldı: komşu bellek alanı boşsa, orası da bu dizinin kapasitesine eklenir)

c) Eğer komşu alan da doluysa, senin de söylediğin gibi, bütün elemanlara fazlasıyla yeten yeni bir yer ayrılır, var olan bütün elemanlar oraya kopyalanır; en sonunda da yeni eleman onların sonuna yerleştirilir

Ancak bunu yazdığım bir programda en sonunda gözlemledim. Ancak anlamadığım bazı şeyler var:

import std.stdio;
 
import std.array;
 
void main()
 
{
 
    int[] a;
 
    int[] c = new int[5];
 
    int b;
 
    writeln("a kapasite: ",a.capacity);
// elemanı artsada hep aynı adresten başlıyor
   // ve b dizisi ile aralarında hep aynı sayıda bayt boşluğu var.
  // a dizisinin adresi neyi gösteriyor. Neyi depoluyor?
    writeln("b kapasite: ","yok normal birlik int");
 
    writeln("c kapasite: ",c.capacity);
 
    writefln("%d",&a);
 
    writefln("%d",&b);
 
    writefln("%d",&c);

a, b, ve c dizileri hiç yer değiştirmiyorlar. Gerektiğinde yer değiştirenler, elemanlar.

Üçünün de aslında dilim olduklarını biliyoruz. Yani dinamik dizilerle dilimler aynı şey. Her ikisinin kendi uzunluğu, yani tür olarak uzunluğu, her zaman için sekizdir (bir gösterge ve bir uzunluk):

import std.stdio;
 
void main()
{
    int[] a = new int[1];
    int[] b = new int[100];
 
    bilgiVer("a", a);
    bilgiVer("b", b);
}
 
void bilgiVer(string isim, ref int[] dizi)
{
    writeln("isim                             : ", isim);
    writeln("dizi türünün bayt olarak uzunluğu: ", dizi.sizeof);
    writeln("dizideki eleman adedi            : ", dizi.length);
    writeln("dizinin adresi                   : ", &dizi);
    writeln("dizideki ilk elemanın adresi     : ", dizi.ptr);
    writeln("&dizi[0]                         : ", &dizi[0]);
}

Bir çıktısı şöyle:

isim                             : a
dizi türünün bayt olarak uzunluğu: 8
dizideki eleman adedi            : 1
dizinin adresi                   : BFF72AC8
dizideki ilk elemanın adresi     : 7F7E40
&dizi[0]                         : 7F7E40
isim                             : b
dizi türünün bayt olarak uzunluğu: 8
dizideki eleman adedi            : 100
dizinin adresi                   : BFF72AD0
dizideki ilk elemanın adresi     : 7F6800
&dizi[0]                         : 7F6800
[code]
Dikkat edersen, dizinin adresi ile ilk elemanının adresi farklı. İlk elemanının adresi iki yolla öğrenilebiliyor: dizi.ptr veya &dizi[0].
Bunun nedeni, dinamik dizilerin (veya dilimlerin) şöyle bir tür gibi düşünülebilmeleridir:
[code]struct Dizi
{
    int * ptr;
    uint length;
}

O yüzden de dizi değişkeninin kendi uzunluğu hep sekiz çıkar. İçinde asıl elemanları gösteren bir gösterge, ve kaç eleman bulunduğunu gösteren bir uzunluk vardır. Kapasite bilgisi ise bellekten elemanlar için ayrılan yerin yakınında gizli bir yere yazılıyor.

O bilginin nereye yazıldığını std.array'in içine bakarak öğrenmeye çalışabiliriz. Ama bilginin çöp toplayıcıya ait bloktan edinildiği gibi bir izlenim edinebiliyorum: :)

_capacity = GC.sizeOf(pArray.ptr) / T.sizeof;

Ayrıca capacity ne olduğunu şöyle düşündüm: capacity ilk adresten sonra kaç bit alanı daha o diziye verileceğini söylüyor.

"Bit" dediğin için söylediğinden tam emin olamıyorum. Ama kapasitenin tanımı çok daha basit: şu ana kadar elinde bulunan belleğe kaç eleman sığdığı. Yani kapasite de eleman adedidir.

Uzunluğu 100, kapasitesi 120 olan bir diziye güvenle 20 adet eleman daha ekleyebiliriz demektir. Çünkü dizi bir noktada ilerisini düşünmüş ve fazladan elemanlar için yer ayırmıştır.

Yukarıda da dediğim gibi, kapasite dolsa bile, komşu alan boşsa, bütün komşu alanı da kendisine ayırarak kapasitesini çok uzuca arttırır.

örneğin kapasitesi 4 olan ancak hiç elemanı olmayan bir  a dizisi için adres şu şekilde olabilir:

00000000 // 1.eleman
00000004 //2.eleman
00000008 //3.eleman
0000000c //4.eleman başladığı yer.
0000000f//4.elemanın bittiği yer.
00000010 // başka bir elemanın başladığı yer.

Ancak bu adresler alındığı halde bu adreslere erişirken hata veriyor. Çünkü işleri boş ?

Kapasite, ilerideki elemanlar için ayrılan yerdir. Söylediğin kapasitesi 4 olan içinde elemanı bulunmayan diziyi şöyle gösterebiliriz:

00000000 // ileride 0 numaralı elemanın geleceği yer
00000004 // ileride 1 numaralı elemanın geleceği yer
00000008 // ileride 2 numaralı elemanın geleceği yer
0000000c // ileride 3 numaralı elemanın geleceği yer
(hepsi o kadar; eğer bundan sonrası müsaitse, 4 ve sonraki numaralı elemanlar buraya gelebilirler)

Dizinin kapasitesinin olması, elemanının olduğu anlamına gelmez. Elemanı olmayan dizide dizi[0] diyemeyiz.

Not: dizilerin nereye yerleşeceğini ve hangi sırada yerleşeceğini derleyici mi karar veriyor yoksa işletim sistemi mi ?

Derleyicinin programın içine gömdüğü kodlar (büyük olasılıkla da çöp toplayıcı) çalışma zamanında belirliyor. İşletim sistemi, istenen belleğin ne için kullanılacağını bilmez. O, yalnızca belirli büyüklükte parçalar (sanırım 4K kadar) olarak verir.

İşletim sistemi karar veriyorsa eğer sırasıyla yerleştirmezse(sanırım popüler olanlar sırasıyla yerleştiriyor.)

Dizilerin sırasız olması mümkün değildir. O veri yapısının tanımı gereği, bütün elemanlar bellekte yan yana dururlar.

nasıl sırayı takip edebiliyor ? (Örne dizi[2] adresinin yerini işletim sistemi nasıl bilebiliyor.) Derleyici ayrıca nasıl yer edinmek istediğini belirtiyor ?

Çöp toplayıcı, işletim sisteminden çok büyük bir alanı bir seferde istiyor; ondan sonra, programın ihtiyaçları doğrultusunda bu alandan parça parça program için ayırıyor, işi bitenleri başka işler için kullanıyor... Daha da bellek gerekirse yine işletim sistemine danışıyor.

Ali
canalpay (Moderatör) #3
Kullanıcı başlığı: Can Alpay Çiftçi
Üye Tem 2009 tarihinden beri · 1133 mesaj · Konum: İzmir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Teşekkür anladım.

Bu belleğe yerleştirme yüzünden sanırım gösterge kullanmak şart. Örneğin bir liste tanımlarken yada bir ağaç yapısı tanımlarken şart ? Ağaç türünü göstergesiz yazamayız(Belki xml gibi yazarak göstergesiz yazarız ?) ?

Ben ağaç yapısına benzer bir yapıyı göstergesiz yazmaya çalıştım ancak olmadı.
acehreli (Moderatör) #4
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Evet, onlari gostergesiz yazmak cok ters olur. Dogalarina aykiri... :)

Heap denen veri yapisi da ikili agaca cok benzer ama o dizi olarak gerceklestirilir. Onu hatirlatirsan yuz yuze de konusuruz. :)

Ali
Doğrulama Kodu: VeriCode Lütfen resimde gördüğünüz doğrulama kodunu girin:
İfadeler: :-) ;-) :-D :-p :blush: :cool: :rolleyes: :huh: :-/ <_< :-( :'( :#: :scared: 8-( :nuts: :-O
Özel Karakterler:
Bağlı değilsiniz. · Şifremi unuttum · ÜYELİK
This board is powered by the Unclassified NewsBoard software, 20100516-dev, © 2003-10 by Yves Goergen
Şu an: 2017-11-22, 06:41:37 (UTC -08:00)