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:
dabs
’ın tanımı aynı kaynak dosyadadeneme
’nin tanımından önce gelir. Bu durumdadabs
’ın tanımı etkisinideneme
’nin içinde de sürdüreceği için hiçbir sorun çıkmayacaktır. Derleyici, yukarıdaki ifadededabs
tarafından beklenen ve geri döndürülen değerin tipinindouble
olduğunu bilecek ve gerekli tip dönüşümlerini yapacaktır.dabs
’ın tanımıdeneme
’nin tanımından sonra gelir. Derleyicidabs
’ı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.- 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 fonksiyonunint
döndürdüğünü varsayması, böylece dedabs
fonksiyonu tarafından döndürülendouble
değerindeneme
tarafındanint
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.