Standart C Programlama Dili


4.2. Fonksiyon Çağrıları

Bir fonksiyonu çağırmak için fonksiyon ismini ve virgülle ayrılıp parantez içine alınmış argümanların bir listesini belirtin. Fonksiyonun argümanları olmasa dahi parantezler bulunmalıdır, böylece derleyici ismin bir değişkeni değil de bir fonksiyonu gösterdiğini anlayabilecektir. Fonksiyon tarafından geri döndürülen değer kullanılabilir veya kullanılmayabilir. Örneğin,

abs(-10);

bir fonksiyon çağrısıdır. Bu noktada kontrol, çağıran fonksiyondan çağrılan fonksiyona aktarılır. -10 değeri abs fonksiyonunun n parametresine atanır. Çağrılan fonksiyon, ya bir return deyiminin yerine getirilmesi yada fonksiyon gövdesinin sağ çengelli parantezine ulaşılması sonucu bittiğinde, kontrol çağıran fonksiyondaki kaldığı yere geri döner. Fonksiyon tarafından bir değer döndürülüyorsa, bu değer çağrının yerini alır. Örneğin,

x = abs(-127);

x’in değerini 127 yapacaktır; oysa daha yukarıda aynı fonksiyona yapılan ilk çağrıda geri döndürülen değer (10) kullanılmamıştı, bu yüzden bu değer kaybolmuştu. Dönüş_tipi void olan bir fonksiyonun bir ifade içinde kullanılmaması gerektiği açıktır.

Bir argüman istenildiği kadar karmaşık bir ifade olabilir. Argüman değerinin tipi fonksiyon tanımındaki karşılık gelen parametre için beklenen tipe uymalıdır, aksi takdirde, aşağıda açıklanacağı gibi, bulunması zor olan bazı hatalar meydana çıkabilir.

Dikkat: Fonksiyon argümanlarının hesaplanma sırası belirtilmemiştir. Örneğin, iki int argümanının toplamını veren topla isminde bir fonksiyonumuzun olduğunu varsayalım. Ayrıca, değeri 5 olan, i adında bir değişkenimiz olsun.

toplam = topla(i--, i);

yazdığımızda toplam’ın değeri ne olacaktır? 9 mu 10 mu? Bu, argümanların hesaplanma sırasına bağlıdır. Değişik derleyiciler değişik şekilde yapabilirler. Bizim sistemimizde, 9 sonucunu almaktayız. Ancak topla(i, i--) çağrısı da 9 sonucunu vermektedir. Böyle durumlarda, derleyicinin nasıl davranacağını tahmin etmek gereksizdir. Zaten, derleme sırasında derleyici bir uyarı mesajı verecektir. Bu uyarıları mutlaka dikkate alıp, yukarıdaki gibi kullanımlardan kaçınılması gerektiğini söylemeye gerek yok tabii.

Fonksiyonlar kullanılmadan önce bildirilmelidirler. Bu kural bazı hataların önlenmesi için titizlikle uygulanmalıdır. C derleyicisi, bildirimi yapılmayan bir fonksiyon kullanımı ile karşılaştığında dönüş tipinin int olduğunu varsayar. Örneğin, aşağıdaki gibi dabs adında bir fonksiyon tanımladığımızı

double dabs (double n)
{
  return (n >= 0.0) ? n : -n;
}

ve onu

void deneme (void)
{
  double d;
  
  d = dabs(-3.14159);
  
}

şeklinde çağırdığımızı düşünün. Üç olasılık vardır:

  1. dabs’ın tanımı aynı kaynak dosyada deneme’nin tanımından önce gelir. Bu durumda dabs’ın tanımı etkisini deneme’nin içinde de sürdüreceği için hiçbir sorun çıkmayacaktır. Derleyici, yukarıdaki ifadede dabs tarafından beklenen ve geri döndürülen değerin tipinin double olduğunu bilecek ve gerekli tip dönüşümlerini yapacaktır.
  2. dabs’ın tanımı deneme’nin tanımından sonra gelir. Derleyici dabs’ın bir fonksiyon çağrısı olduğunu tanıyacak, ancak, henüz dönüş tipinin ne olduğunu bilemeyeceği için, int döndüren bir fonksiyon olduğunu varsayacaktır. Daha sonra, dabs’ın gerçek tanımıyla karşılaştığında, bir sorun ortaya çıkacaktır, çünkü double döndüren bir fonksiyon olarak tekrar tanımlamaya çalışacaktır. Bu durumda bir hata mesajı verilecektir.
  3. En kötü durum, dabs’ın tanımı ayrı bir kaynak dosyada verildiği zaman ortaya çıkacaktır. Ayrı ayrı derlemeden dolayı, derleyici uyuşmazlığı bulamayacaktır. Hatta bağlayıcı bile bir şey yapamayacaktır. Programın yürütülmesi esnasında anlamsız sonuçlar elde edilecektir. Bunun nedeni, yine, derleyicinin tanımı veya bildirimi yapılmamış olan fonksiyonun int döndürdüğünü varsayması, böylece de dabs fonksiyonu tarafından döndürülen double değerin deneme tarafından int gibi yorumlanmasıdır. Birçok derleyici yapılan bu tip varsayımları, bir uyarı mesajı şeklinde kullanıcıya bildirirler. Bu tip uyarıları mutlaka dikkate alın, çünkü varsayımlar her zaman sizin düşündüklerinizle uyuşmayabilir.

Yukarıdaki 2 ve 3’teki sorunları çözmek için aşağıdaki şekil önerilmektedir:

void deneme (void)
{
  double d, dabs(double);
  
  d = dabs(-3.14159);
  
}

Yukarıdaki bildirim (buna ayrıca fonksiyon prototipi de denir) deneme fonksiyon tanımının dışında ve önünde de yazılabilir. Böyle fonksiyon prototiplerinin tamamını programın başlangıcında veya böyle fonksiyonları kullanan her kaynak dosya tarafından #include kullanılarak içerilecek başlık dosyalarına yazmak sıkça kullanılan bir yöntemdir. Standart kütüphane fonksiyonlarının bildirimlerini yapan standart başlık dosyaları buna iyi bir örnek oluştururlar. Bunlar, derleyicilerin çeşitli hataları yakalamalarını veya gerektiği zaman tip dönüşümleri yapmalarını sağlar. Örneğin, yukarıdaki bildirimden sonra, derleyici,

d = dabs(5);

ifadesindeki 5 tamsayısını dabs’a geçirmeden önce double’a dönüştürecektir. Ancak

d = dabs("bes");

için bir hata mesajı verecektir, çünkü bu durumdaki karakter göstergesi double’a dönüştürülemez.

Argümanlar çağıran fonksiyondan çağrılan fonksiyona geçirildiğinde, doğal tiplerine (örneğin short’tan int’e) dönüştürürler. Eğer fonksiyon kısa bir tip bekliyorsa, bu argümanın kısa tipe dönüştürülmesi gerekecektir. Böylece, eğer özel bir neden yoksa, fonksiyon parametrelerinin bu doğal tiplerden olması daha iyi olacaktır. Genelde, dönüş tipleri için de aynı şey söz konusudur.