Standart C Programlama Dili


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 uzunlugudur */
{
  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 “int a[]” ile “int *a” aynı şeydir, yani 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 “int *a” ş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 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.