4.2.2. Referans İle Çağrı
Çağrıyı yapana bilgi döndürmek istediğimizde
ne yapmamız gerekir? Bir yöntem fonksiyonun dönüş değerini kullanmaktır.
Şimdiye kadar olan örneklerimizde bu çok sık kullanılmıştı. Fakat, birden
fazla döndürülecek değer varsa o zaman ne yapacağız? Bu durumda, referansla
argüman geçirmenin bir yolunu bulmamız gerekecektir. Aslında bunu nasıl
yapacağımızı da biliyoruz. Daha önceki bölümlerde programa değer girmek
için scanf
fonksiyonunu kullanmıştık. Bu fonksiyon
argümanlarına yeni değerler atar. Bu fonksiyonu ilgilendirmediğine
göre, argümanların eski değerlerini geçirmek yerine,
adreslerini geçiririz. Bu amaçla da, adres alma
(&
) işlecini kullanırız.
Şimdi referans ile çağrı yöntemini göstermek için bir örnek
vermenin zamanıdır. İki int
argümanının değerlerini
değiş tokuş eden bir fonksiyon yazmak istediğimizi varsayın.
void degis (int x, int y) { int t; t = x; x = y; y = t; }
fonksiyonu bu işi yapacaktır, ancak sadece
yerel olarak! Parametrelerin yeni değerleri çağıran fonksiyona geri
iletilmeyecektir. Değerleri geçirmek yerine, yukarıda anlatıldığı gibi
değişkenlerin adreslerini geçirmemiz gerekir. Böylece, a
ve
b
’nin int
değişken olduğu
degis(&a, &b)
şeklinde bir çağrı gerekecektir. Ancak
degis
fonksiyonu da bu göstergeleri
kabul edecek şekilde yeniden düzenlenmelidir. Herşeyden önce, parametre
bildirimleri değiştirilmelidir; yeni parametre değişkenleri değerler
yerine adresleri saklamalıdır. Bildirim şöyledir:
int *xg, *yg;
ve, örneğin, xg
değişkeninin bir
int
göstergesi olduğu anlamına gelir. Şimdi
bütün x
ve
y
’leri sıra ile *xg
ve *yg
ile değiştirirsek degis
’in doğru tanımını elde ederiz:
void degis (int *xg, int *yg) { int t; t = *xg; *xg = *yg; *yg = t; }
Unutulmaması gereken bir nokta, dizi argümanlarının her zaman referans
ile geçirildiğidir. Örneğin, argüman olarak verilen diziyi sıraya sokan
sirala
adında bir fonksiyonumuz olduğunu varsayalım. Fonsiyonun
bildirimi şöyle bir şey olacaktır:
void sirala (int a[], int n) /* n dizinin uzunluğudur */
{
sıralama işlemi
}
Bu durumda,
sirala(dizi, eleman_sayisi);
şeklinde bir çağrı, dizinin taban adresini
fonksiyona geçirecektir. sirala
’daki a
geçirilen dizinin yerel bir kopyası değildir. a
dizisi
nin uzunluğunun belirtilmemesinden, dizi
için bir yer ayrılmadığını anlayabilirsiniz. sirala
fonksiyonu
içinde a
’ya yapılan bütün referanslar aslında
dizi
’ye yapılmaktadır, bundan
dolayı değişiklikler de orada yapılmaktadır. Bunun nedeni dizi
ile &dizi[0]
’ın tamamen aynı şey olmasıdır. Bu
iyi bir özelliktir, çünkü aksi takdirde bütün dizinin yerel bir diziye
aktarılması gerekecekti; bu da hem bilgisayar zamanı hem de bellek
harcayacaktı.
Aslında, parametre_bildirimlerinde
ile
int a[]
aynı şeydir, yani int *a
a
bir göstergedir. Örneğin, bir dizinin elemanlarını toplayan bir
fonksiyonumuz var diyelim. Bunu şu şekilde tanımlayabiliriz:
int topla (int a[], int n) { int s = 0; while (n--) s += a[n]; return s; }
ve dizi
’nin bir tamsayı dizisi,
uzunluk
’un da toplanacak elemanların sayısı olduğu
topla(dizi, uzunluk)
şeklinde bir çağrı yapabiliriz.
dizi
isminin bir gösterge olduğu,
bundan dolayı topla
’nın a
’da elde ettiği
şeyin bir gösterge olduğuna dikkat edin. a
’nın
şeklinde bildirilmesi daha iyi
olacaktı. Aslında derleyici de aynı şeyi düşünmekte ve fonksiyon parametreleri
için iki bildirimi tamamen eşdeğer kabul etmektedir. Bunu görmek için
aşağıdakini karşılaştırın:int *a
int topla (int a[], int n) { int s = 0; while (n--) s += *a++; return s; }
Atama deyiminde, *a++
, *(a++)
anlamına gelir, bu da (*a)++
’dan tamamen farklıdır,
çünkü bu sonuncusu dizinin ilk elemanını sürekli olarak artırmaya yarar,
yani a[0]++
. topla
’nın bu yeni şeklinin
ilk verilenle aynı işi yaptığını, ancak daha verimli olduğunu gösterin.
Ayrıca, topla
’ya dizinin taban adresi yerine başka
bir adresin de verilebileceğini akıldan çıkarmayın. Örneğin
topla(&dizi[2], uzunluk-2)
ve
topla(dizi+2, uzunluk-2)
topla
’nın dizinin üçüncü elemanından
başlayarak, yani ilk iki elemanı atlayarak, işlemi yapmasını sağlayan iki
eşdeğer çağrıdır.
Bir fonksiyon parametresinin bildirimi yapılırken
const
tip niteleyicisi de kullanılabilir.
Bir dizinin fonksiyon gövdesi içinde değiştirilmeyeceğini göstermesi
açısından, dizi parametrelerinin bildirimlerinde yararlanılabilir.