|
Değer İle Çağırma - Referans İle Çağırma Kavramları |
|
Gönderiliyor lütfen bekleyin... |
|
|
Bu yazımızda,
"değer ile çağırma" ve "referans ile çağırma" kavramlarının
anlamlarına ve uygulamada ne şekilde karşımıza çıkacaklarına değineceğiz. Değer
ile çağırma ve referans ile çağırma terimlerinin İngilizce karşılıkları sırasıyla
"Call by Value" ve "Call by Reference"dır. Peki bu terimler
ne anlama gelmektedir? Örneğin bu terimler ile parametre değişkenlerinin fonksiyonlara
aktarımı sırasında karşılaşırız. int türden bir parametre alan bir fonksiyona,
çağrımdan önce tanımladığımız int türden bir değişkeni parametre değişkeni olarak
geçtiğimiz zaman, fonksiyonun parametre değişkeni için geçici bir nesne yaratılır
ve bizim fonksiyona göndermiş olduğumuz değişkenin değeri bu geçici nesneye
aktarılır. Dolayısıyla fonksiyonun parametresinin değeri ile bizim fonksiyona
geçtiğimiz değişkenin değeri aynı olur. Ancak bu iki nesne aynı nesne değildir.
Bellekte bu noktada iki tane birbirinden farklı ancak aynı değere sahip nesne
bulunmaktadır. Bu durum "değer ile çağırma" olarak bilinir. Bir örnekle
bu durumu açıklayalım :
int IslemYap(int
parametre)
{
    return parametre * parametre;
}
//...
int sonuc, degisken = 5;
sonuc = IslemYap(degisken); |
Örnekte tanımladığımız
fonksiyon int türden bir parametre alıyor. Fonksiyon, bu parametrenin karesini
alarak bu değere geri dönüyor. Örneğin devamında, "sonuc" ve "degisken"
isimlerinde int türden iki nesne tanımlıyoruz. Daha sonra "degisken"
nesnesini, IslemYap() fonksiyonuna parametre olarak geçerek geri dönen değeri
"sonuc" nesnemize atıyoruz. Fonksiyonu çağırdığımız noktada, fonksiyonun
parametresi için int türden geçici bir nesne yaratılır. Bu nesneye "degisken"
nesnesinin değeri, yani 5 değeri atanır. Fonksiyon içerisindeki işlemler bu
geçici nesne üzerinde yapılır. Fonksiyon return ifadesi ile son bulduktan sonra
bu geçici nesne de yok edilir. Peki fonksiyonu şu şekilde yazmış olsaydık ne
ile karşılaşacaktık?
void IslemYap(int
parametre)
{
    parametre *= 2;
}
int main()
{
    int degisken = 5;
    printf("ilk
deger :%d\n",degisken);
    IslemYap(°isken);
    printf("cagridan sonraki deger :%d\n",degisken);     return 0;
}
|
Burada IslemYap()
fonksiyonunda, fonksiyonun parametre degiskeninin değeri değiştirilmiştir. Fonksiyon
herhangi bir değer geri döndürmemektedir. Böyle bir fonksiyonun yazılmasındaki
amaç, büyük ihtimalle parametre değişkeninin değeri değiştirmektir. Kod içerisinde,
fonksiyonu değeri 5 olan bir değişken ile çağırıyoruz. Değişkenin değerinin,
fonksiyonun yaptığı işe göre 10 olmasını bekliyoruz. Ancak ikinci printf() çağrısı
ile de ekrana 5 yazılıyor. Bunun nedenini şöyle açıklayabiliriz : Fonksiyonun
parmetre değişkeni olan nesne, fonksiyon çağrıldığında derleyici tarafından
tanımlandığı türde yaratılır. Bu nesneye, fonksiyon çağrılmasında kullanılan
nesnenin değeri aktarılır. Bu noktada bizim fonksiyonumuzun parametre değişkeninin
değeri 5’tir. Fonksiyon bloğu içerisinde bu geçici nesnenin değeri "parametre
*= 2;" ifadesi ile 10 yapılır. Fonksiyonun bloğu sonlandığında bu geçici
nesne de yok edilir. Yani değeri 10 olan nesnemiz artık yoktur. Bizim fonksiyonumuzu
çağırırken kullandığımız nesnenin değeri ise hala 5’tir. Çünkü biz bu fonksiyonu,
"call by value" yöntemine göre yani "değer ile çağırma"
yöntemine göre tanımlamışız. Bu durumu şekille ifade edelim :
   
Yukarıdaki şekillerde
yazdığımız kodda tanımlanan nesnelerin bellekteki temsili görünümlerini görüyoruz.
degisken isimli nesnenin bellekte 1000 adresinde ayrıldığını varsayalım. (Buradaki
adres değerleri tamamen örnek olarak verilmiştir.) Fonksiyonun ana bloğuna girildiğinde
parametre değişkeni için 3000 adresinin ayrıldığını düşünelim. Bu durumda fonksiyon
içerisinde paremetre değişkeni üzerinde yapılan değişikliklerden belleğin 3000
adres bölümü etkilenecektir. degisken isimli nesnenin bulunduğu 1000 adres değerine
sahip bölge bu değişiklikten etkilenmeyecektir. Fonksiyon sonlandığında parametre
değişkeni için ayrılan bölge de boşaltılacağı için değişiklikler sonucunda elde
ettiğimiz değeri de kaybetmiş olacağız.
Bir nesnenin değerini,
bir fonksiyon içerisinde değiştirmek istiyorsak; bu fonksiyona nesnenin değerini
değil, referansını göndermeliyiz. Başka bir deyişle, fonksiyonumuz bu nesnenin
bellekteki konumuna giderek yapacağı değişiklikleri orada yapmalıdır. Aksi takdirde
hiçbir nesnenin değerini fonksiyon içerisinde değiştiremeyiz. Sadece bu nesnenin
değerini kullanarak, geçici nesneler üzerinde işlem yapabiliriz. Yukarıda yapmak
istediğimiz işlemi şöyle bir kod yazarak gerçekleştirebiliriz :
void IslemYap(int
*parametre)
{
   *parametre *= 2;
}
int main()
{
    int degisken = 5;     printf("ilk
deger :%d\n",degisken);
    IslemYap(°isken);
    printf("cagridan sonraki deger :%d\n",degisken);     return 0;
}
|
IslemYap() fonksiyonunun
bu hali, parametre değişkeni olarak int türden bir adres almaktadır. Bu adresteki
değere (*parametre) ifadesi ile erişiyoruz ve 2 ile çarpıp fonksiyonu sonlandırıyoruz.
Şimdi fonksiyonu çağırırken parametre değişkeni olarak degisken nesnesini değil,
bu nesnenin adresini gönderiyoruz. Dolayısıyla fonksiyon içerisindeki değişiklikler
bu adres üzerinde gerçekleiyor. Fonksiyondan çıkıldığında ise bu adres bölgesi
henüz boşaltılmadığı için degisken isimli nesneye ulaştığımızda 10 değerini
elde ediyoruz. Bu şekilde çağırma "Call by Reference", yani "referans
ile çağırma" olarak bilinir. Bu çağırmada fonksiyona parametre değişkeni
olarak nesnenin değeri değil, kendisi gönderilmiş olur. Bu değeri elbette fonksiyondan
return ifadesi ile geri döndürerek de elde edip çağırdığımız yerde bir değişkene
atayabilirdik. Ancak bir fonksiyondan birden fazla sayıda değer elde etmek istiyorsak
bu işlem için referans ile çağırma yöntemini tercih etmemiz gerekir. return
ifadesi ile fonksiyondan ancak bir değeri geri döndürebiliriz. Örneğin bir fonksiyondan
bize bir sayının hem karesini hem de küpünü göndermesini istiyorsak return ifadesine
hangi değeri yazacağız? Bu işlem için fonksiyona parametre değişkeni olarak
iki adres gönderirsek, elde ettiğimiz değerleri bu adres bölgelerine yazıp fonksiyon
çağrısından sonra bu bölgelere erişebiliriz :
void KareVeKupAl(int
sayi,int *kare, int *kup)
{
    *kare = sayi * sayi;
    *kup = sayi * sayi * sayi;
}
int main()
{
    int *karesi, *kubu;
    int sayi = 5;
    karesi =
(int *)malloc(sizeof(int));
    kubu = (int *)malloc(sizeof(int));
    printf("Sayi
:%d\n",sayi);
    KareVeKupAl(sayi,karesi,kubu);
    printf("Karesi :%d\n",*karesi);
    printf("Kubu :%d\n",*kubu);
    return 0;
}
|
KareVeKupAl fonksiyonu
parametre değişkeni olarak, karesi ve küpü alınacak sayısı, daha sonra elde
ettiği değerleri yazacağı adresleri alıyor. Fonksiyonu çağırırken karesini almak
istediğimiz sayıyı ve elde edilen değerleri saklamak istediğimiz ve çağrıdan
önce tahsis ettiğimiz güvenli adres bölgelerini gönderiyoruz. Fonksiyon çağrısından
sonra bu adres bölgelerine erişerek fonksiyondan elde edilen değerleri yazdırıyoruz.
Bu şekilde birden fazla sayıda değer döndürmesi gereken fonksiyonların bu şekilde
yazılması gerekir. (Ya da bir yapı tanımlayarak geri dönüş değerlerini yapının
elemanlarında tutabilirsiniz.)
Diziler üzerinde
birtakım işlemler yapan fonksiyonlar yazmak istediğimizi düşünelim. Örneğin
ilk olarak int türden bir dizinin tüm elemanlarının değerlerini birer artıran
bir fonksiyon yazalım. Bunu değer ile çağırma yöntemine göre yazamayız. Diziler
her zaman fonksiyonlara referansları ile aktarılır. Bunun yerine dizinin başlangıç
adresini ve uzunluğunu parametre değişkeni olarak alıp, fonksiyon içerisinde
dizi elemanları üzerinde dolaşarak doğrudan elemanlara erişir ve değerleri güncelleriz.
Bu fonksiyonu yazalım :
void IslemYap(int
*dizi,int uzunluk)
{
    int i;     for (i =
0; i < uzunluk; ++i)
       dizi[i]++;
}
int main()
{
    int i;
    int dizi[] = {1,2,3,4,5};
    printf("dizinin
ilk hali :\n");
    for (i = 0; i < 5; ++i)
       printf("%d~",dizi[i]);
    IslemYap(dizi,5);
    printf("\ndizinin
fonksiyon cagrildiktan sonraki hali :\n");
    for (i = 0; i < 5; ++i)
       printf("%d~",dizi[i]);
    return 0;
}
|
Bu fonksiyonda,
parametre değişkeni olarak int türden bir adres ve bir de uzunluk bilgisi alıyoruz.
Fonksiyon bloğu içerisinde bu adresten itibaren uzunluk değeri kadar adım adım
ilerliyor ve ilgili adreslerdeki değerleri birer artırıyoruz. Örneğin dizi isminde
5 elemanlı bir dizi tanımladık ve fonksiyona bu dizinin başlangıç adresini gönderdik.
Hatırlayacağınız üzere C dilinde dizi isimleri, dizilerin başlangıç adreslerini
belirtir. Fonksiyondan çıkıldıktan sonra artık dizimizin elemanlarının değerleri
birer artırılmış durumdadır.
Bu yazımızda değer
ile çağırma ve referans ile çağırma kavramlarına ve ne zaman hangi yöntemi kullanmamız
gerektiği konusuna değindik. Sonraki yazılarımızda görüşmek dileğiyle mutlu
günler dilerim.
Makale:
Değer İle Çağırma - Referans İle Çağırma Kavramları C ve Sistem Programlama Çiğdem Çavdaroğlu
|
|
|
-
-
Eklenen Son 10
-
Bu Konuda Geçmiş 10
Bu Konuda Yazılmış Yazılmış 10 Makale Yükleniyor
Son Eklenen 10 Makale Yükleniyor
Bu Konuda Yazılmış Geçmiş Makaleler Yükleniyor
|
|