C pointerlara giriş

Merhaba.

Bu yazı C’de pointerları çalışırken aldığım notlardır.

Bu pointerların vikipediadaki tanımı:

Programlama dillerinde bellek adreslerini saklayan değişkenlere verilen genel isim. Bir programlama dilinde herhangi bir değişkeni tanımladığınızda hafızada ona bir yer ayrılır. İşaretçiler bu hafıza alanlarının adreslerini tutarlar

Bu konuyu anlamamda yardımcı olan şeylerin başında değişkenlerin tanımı geliyor. İşte birbaşka tanım

Değişkenler, bir bilgisayar programında değerleri saklamak için kullanılan, bilgisayar bellek konumlarına verdiğiniz adlardır.

Hepinizin zaten bildiği gibi bir bir programlama dilinde değişken tanımladığımızda değeri memory’e konulur.İsmi ilede Bu değişkenin değerini memory’den alırız.

Basitçe aşağıdaki gibi yazacağımız bir program ile memory’deki yerimizi görelim.

#include <stdio.h>

int main() {

short age = 20;
printf("Address of age variable %p \n", &age);
return 0;
}

Çıktı tabiki her çalıştırdığımızda farklı olucaktır.

Örnek çıktı:

Address of age variable 0x7ffe5886c856

Bu örnekte 0x7ffe5886c856 değeri bizim ram bellek üzerinde short tipindeki age değişkenimiz için işletim sisteminin ayırdığı alan.

Pointer

Pointer, başka bir değişkenin adresini, yani bellek konumunu(az önce ekrana yazdırdık) tutarlar.

Pointerlar değişkenler gibi tanımlanırlar.Sadece başlarında *(asteriks) işareti bulunur.

Hemen bir örnek yapalım.

short *age_p;    /* pointer to an short */
printf("Address of age_p variable %p \n", &age_p);

Daha önce üzerinde çalıştığımız programa yukarıdaki satırları ekliyorum.

Address of age variable 0x7ffe1b87255e
Address of age_p variable 0x7ffe1b872560

Gördüğünüz gibi age_b memory’de farklı bir yerde.Aralarındaki fark değişken direk veriyi tutuyor.Pointer ise asıl verinin durduğu değişkenin adresini tutuyor.

Şu ana kadar elimizde short tipinde bir değişken ve onun adresini tuması için tanımladığımız bir pointer var. Aşağıdaki blokta yapacağımız tanımlama ile artık memory’de age_p’nin içerisinde age değişkeninin adresi duruyor. Başına * işareti koayarak değerini yazarsanı veya değiştirirseniz. Aslında age değişkeninin değerini değiştireceksiniz.

age_p = &age;
printf("Value of age_p pointer %d \n", *age_p);

Valıe of age_p pointer 20

Programın son halinde ise memory aşağıdaki gibi olucaktır.

Programın son halinde ram bellek temsili.

Gördüğünüz gibi aslında pointer olarak tanımladığımız değişken asıl değeri tutan değişkenin adresini tutuyor. * işareti ilede eriştiğimizde bu alana gidip buradaki değer ile işlem yapıyor.

Basitçe neden pointler kullanıyoruz.

— Aklımıza gelen ilk madde bellek tasarrufu.Anlaşılır olması açısından bu örneği short gibi küçük bir veri tipi ile yaptık. Peki ya daha büyük veri tipleri ile çalışsaydık.Bunları fonksiyonlara parametre olarak geçtiğimiz zaman compiler bu veriyi memory üzerinde kopyalayacaktır. Buda büyük objelerde bellekteki verimi düşürecektir. Bunu yerine eğer referansını geçerseniz fonksiyonun içerisindeki işlem doğrudan memory’deki yerinde yapılacaktır.Memory’deki tekrarları engellememizi sağlar. Ayrıca bknz( https://metinagaoglu.com/call-by-value-call-by-reference )

İterasyon sırasında hızı artırabilirler: örneğin bir arrayi gezmek isterseniz, indis değerini yinelemek için sadece bir for döngüsü yazarsınız, ancak gerçekte bellek adresini artırmış olursunuz. Dolayısıyla, performansa duyarlı bir kodlama yapıyorsanız, önce indisi artırmak ve ardından bellek adresini güncellemek yerine, pointer ile doğrudan bir sonraki bellek konumuna gidip performans iyileştirmesine erişebilirsiniz.

Yapılan değişiklikler her yerde aynıdır: bağımsız thread’ler ile çalışan bir programınız olduğunu varsayalım.Her biri için aynı verilerin kopyasını oluşturmak yerine, onlara orijinal verilerin adresini sağlarsınız ve böylece değerde herhangi bir değişiklik yapılırsa, her thread en son güncellemeleri alır.

Şimdilik bu kadar.Pointerlar ile ilgili aritmetiği vb. gibi konulardada özet yazılar yazmayı planlıyorum.

Kaynaklar

https://www.tutorialspoint.com/cprogramming/c_pointers.htm

https://www.quora.com/Why-we-use-POINTERS-in-programming-whats-the-practical-use

Call by value & call by reference

Selamlar

Bu konu hakkında yazılmış birçok makale var.Bende kendim daha iyi anlamak için kendi bloguma karalama tarzında bir yazı yazacağım.

Yazdığımız programlama dillerinde hepimiz fonksiyonlar/metodlar tanımlıyoruz. Bunlara parametre geçiyoruz bazen dönüş değeri olarak , bazen ise verdiğimiz parametreler üzerinde değişiklik yapıp sonucunu alıyoruz.

Programlama dillerinde, fonksiyonlar iki şekilde çağrılabilir: Call by Value ve Call by Reference.

Genelde fonksiyon method tanımlamalarınıza extra bildirmediğiniz sürece “reference by value” yaklamşımı kullanılır. Bizde ilk önce bunu bir örnek ile göreceğiz.

Call by value

Buraya kadar tamam. Normal şartlarda bu nasıl gerçekleşiyor basit bir örnek yapalım.İlk örneğimizde 2 değişkeni birbiriyle değiştiren klasik bir swap fonksiyonu yazcağız.Örneğimizi inceleyelim.

#include <stdio.h>


void swap(int a,int b) {
    int temp;
    temp = a;
    a = b;
    b = temp;
}

int main() {

    int a = 50 , b = 20;
    printf("a: %d \t b: %d \n",a,b);

    /* calling a function to swap the values */
    swap(a, b);

    printf("a: %d \t b: %d \n",a,b);

    return 0;
}

Yukarıdaki kodu incelersek standart bir swap fonksiyonumuz var. Ancak bunu çağırsak bile çıktı değişmiyor. a ve b değişkenlerimiz değerlerini koruyorlar.

metinagaoglu@linux-mint:~/Desktop/C/refs$ ./ref
a: 50 b: 20
a: 50 b: 20

Reference by value’da bir değişkenin değerini bufonksiyonun parametresine kopyalar. Bu nedenle, ana işlevin parametresinde yapılan değişiklikler bağımsız değişkeni etkilemez.Saddece fonkssiyonun içerisinde geçerliğidir.Aynı değerden memory’de 2 tane vardır. Parametre olarak geçilenler direk değerleri kopyalanmıştır.

Yaptığınız değişiklikler kopyalanan değerler üzerinde yapılır.Foksiyon dışındakiler etkilenmez.Fonksiyo çalıştıkran sonra kopya silinir ve memory’de sizin geçtikleriniz aynen duruyordur. Bu yüzden çıktılarımız aynı.

Call by Reference

Call by reference ta ise fonksiyona parametre olarak referencelarını geçiyoruz.Yani memory’de nerde olduklarını. Fonksiyonun içerisinde ise bu değerler ile memory’deki yerini bildiğimiz değişkendeki değerlerle gene istediğimiz işlemi yapabiliyoruz. Diğer yaklaşımdan farklı değerler kopyalanmadığı için yaptıklarınız her değişiklik değeri değiştiriyor.

Az önce yaptığımız swap fonksiyonunu şimdide call by reference ile yazalım.

#include <stdio.h>

void swap(int *a,int *b) {
int temp;
temp = *a;
*a = *b;
*b = temp;
}

int main() {

int a = 50 , b = 20;
printf("a: %d \t b: %d \n",a,b);

/* calling a function to swap the values */
swap(&a, &b);

printf("a: %d \t b: %d \n",a,b);


return 0;
}

Bu seferki durumda ise gördüğünüz gibi main içerisinde swap fonksiyonmuza & işareti ile referansını geçtik. İçeride ise parametre olarak geçilen değerin referansı ile memorydeki adresine ulaştık ve o değerler üzerinde işlem yaptık.

metinagaoglu@linux-mint:~/Desktop/C/refs$ ./ref
a: 50 b: 20
a: 20 b: 50

Tabiki biz bu yazıda gene kolayına kaçtık ve 2 integer değişkeni kullandık.Ancak örnekleri bununla sınırl tutmayın.Elimizde bir array veya linkedlist vs türünde büyük bir değer varsa ve bunu üzerinde işlem yapıcaksak şeklindede düşünebilirsiniz.

Ne olduğunu kabaca anladığımıza göre biraz avantaj ve dezavantajlarından bahsedelim.

Call by value avantajları

  1. Parametre olarak geçilen ana değişkenler değişmez.
  2. Takibatı ve debug yapması daha kolaydır.Ana değişkendeki değerleri değiştirmediği için fonksiyon içinde daha rahat geçici işlem vs yapılabilir.

Dezavantajları

  1. Hafıza açısından verimli değildir.Aynı değişken için oluşturulmuş iki adet kopya vardır..

Call by reference avantajları

  1. Fonksiyon argümanın değerini değiştirebilir.
  2. Bellek alanından tasarruf etmenize yardımcı olur. Tek bir değeri tutmak için yinelenen veriler oluşturmaz.
  3. Bu yöntemde işlem yapılan parametrenin bir kopyası yoktur. Bu nedenle çok hızlı işlenir.

Dezavantajları

  1. Referans alan bir fonksiyonunu parametresinin boş olmadığından emin olması gerekir.
  2. Çok iş parçacıklı programlarla çalışırken tehlikelidir.
  3. Takibatı ve debug’ı bi nebze daha zordur.

Göz atmanızı tavsiye ettiğim kaynaklar:

https://stackoverflow.com/questions/40185665/performance-cost-of-passing-by-value-vs-by-reference-or-by-pointer

https://www.geeksforgeeks.org/difference-between-call-by-value-and-call-by-reference/

https://www.guru99.com/call-by-value-vs-call-by-reference.html