Managing Memory and Low Level Data Structures

Kategori: C++ , 15 Kasım 2019 , JanFranco


Önceki bölümlerde vector, list gibi yapıları görmüştük. Bu bölümde bu tarz veri yapılarının derinine ineceğiz. Pointerları ve dizileri göreceğiz. Pointerlar ile başlayalım.

Pointerlar objelerin adreslerini gösterirler. Her objenin bellekte bir adresi vardır. Objelerin adreslerini adres operatörünü (&) kullanarak alabiliriz. Örneğin x bir obje ise, &x x objesinin adresidir. Eğer x bir adres ise *x objenin kendisidir. & karakteri adres operatörü, * karakteri dereference operatörüdür. Bir örnek yapalım:


int main()
{
    int x = 5;
    int* p = &x;
    cout << "x = " << x << endl;
    *p = 6;
    cout << "x = " << x << endl;
    return 0;
}
Bu programın çıktısı sırasıyla x = 5 ve x = 6'dır. Burada x değerini 5 atadık. Daha sonra x'in adresini p pointer'ına atadık. *p artık x'in değerini gösteriyor. Ve bu değeri p aracılığı ile bu şekilde değiştirebiliriz. Bir objeyi point edebileceğimiz gibi fonksiyonları da point edebiliriz:


int (*fp)(int);
fp fonksiyonu gösteren bir pointer'dır ve argüman olarak int tipinde değer alır ve int tipinde değerler return eder. Örnek yapalım:


int next(int n){
    return n + 1;
}

fp = &next; // is equal to fp = next;

int i = 3;
i = fp(i); // is equal to i = (*fp)(i);
Dizilerden de bahsedelim. Diziler aynı tip elemanları barındıran, dillerin temel öğelerinden biridir. Dizilerin boyutları başlangıç anında belirtilmelidir. Üç boyutlu uzayda bir noktanın koordinatlarını tutan bir dizi oluşturalım:


double coords[3];

// is equal to:

const size_t NDim = 3;
double coords[NDim];
Arrayler yani diziler ve pointerlar her zaman ilişkilidir. Dizinin adını çağırdığımızda bize ilk elemanının adresini verir. Yani dereference operatörü ile ilk elemanı alabiliriz:


*coords = 1.5;
Bu satır ile ilk elemanı 1.5 olarak güncelledik. coords bize dizinin ilk elemanın adresini veriyor, *coords ise objeyi. Bu özellikler sayesinde dizinin diğer elemanlarına şu şekilde erişebiliriz:


*(coords + 1)
Adresi bir artırdık ve o adresteki objeyi deference operatörü ile aldık. Bu kullanım ile aynı anlama sahip daha kolay bir kullanım da mevcuttur:


coords[1]; // is equal to *(coords + 1)
Dizilerin elemanlarını ilk oluşturduğumuzda belirleyebiliriz. Eğer direk bu şekilde initialize edersek, dizinin boyutunu girmemize gerek kalmaz, compiler girdiğimiz elemanları sayar ve dizinin boyutunu belirler:


const int month_lengths[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
Burada month_lengths[0] Ocak ayını, month_lengths[11] ise Aralık ayını temsil eder. Stringler de aynı zamanda bir dizidir. Tüm elemanları const char olan özel bir dizidir. Stringlerde yani karakter dizilerinde, son eleman compiler tarafından otomatik eklenir: '\0'. Bu karakter ile dizinin sonunu bulabiliriz. kütüphanesindeki strlen() fonksiyonu stringlerin uzunluğunu return eder. Bunu kendimiz de yazabiliriz:


size_t strlen(const char* p)
{
    size_t size = 0;
    while(*p++ != '\0')
	size++;
    return size;
}
Artık başlıkta da belirttiğimiz hafıza yönetimine geçebiliriz. Şuana kadar tüm uygulamalarımızda otomatik hafıza yönetimini kullandık. Aşağıdaki kullanımlar otomatik yönetime örnektir;


int x;
int arr[10];
Burada int x dediğimizde hafızadan 4 byte yer otomatik olarak ayırtılacak. int arr[10] dediğimizde hafızada otomatik olarak 10 * 4 = 40 byte kadar yer ayırtılacak.

Eğer T bir obje türü ise, new T şeklinde bir kullanımda bir T objesi allocate edilir. Değeri default olarak initialize edilir ve bir pointer return edilir. Obje hafızada program kapanana kadar veya delete komutu ile silinine kadar kalır. delete komutunu kullanabilmek için pointer'ın bir obje veya 0 değerini göstermesi gerekir. Bir örnek yapalım:


int* p = new int(42);
++*p; // p is now 43
delete p;
p pointer'ının gösterdiği yerdeki int objesi silindi. p artık bir invalid pointer. Aynı olayı arrayler ile de yapabiliriz:


T* p = new T[n];
vector<T> v(p, p + n);
delete[] p;
Burada T objesinden n kadar nesne oluşturduk. Daha sonra bu objeleri vektöre attık. Daha sonra p pointer'ının gösterdiği diziyi sildik. delete yerine delete[] komutu kullanmamızın sebebi dizinin ilk elemanını değil, bütün diziyi silmek istediğimizdendir.


Sonraki Yazı: Defining Abstract Data Types
Yorumlar

Henüz bir yorum bulunmuyor.