Fonksiyonlar, parametre
parantezleri içerisinde belirtilmiş argüman sayısı kadar parametre değişkenine
sahip olabilirler. Dolayısıyla biz bir fonksiyonu çağıracağımız zaman, bu fonksiyona
aktaracağımız parametre değişkenlerinin sayısı bellidir. Ancak C dili, argüman
sayısı değişebilen fonksiyonlar tanımlanmasına da olanak tanır. C dilinde fonksiyon
tanımlamanın en genel biçimi şöyledir :
[geri_dönüş_değeri_türü]
<fonksiyon_adı> ([parametre_değişkenleri])
{
//fonksiyonun yaptığı işler
}
Fonksiyon tanımlaması
sırasında, fonksiyona aktarılacak parametre değişkenlerinin sayısı da belirtilmiş
olur. Örneğin :
int topla
(int sayi1, int sayi2)
{
return sayi1 + sayi2;
}
şeklinde bir fonksiyon
tanımlanmışsa, çağrım sırasında fonksiyona iki tane parametre değişkeni aktarılır.
Peki, öyle bir durumla karşı karşıya kalalım ki bu sayı değişebilir olsun. Bu
sayı kimi durumlarda iki, kimi durumlarda üç vs şeklinde olsun. Bu durumda ne
yapacağız ? Bu sorunun cevabı, bugünkü makalemizin konusunu oluşturuyor. C dilinde,
parametre değişkeni sayısı değişken olan fonksiyonlar yazılabilir. Bu konuda
iyi bilinen bir örnek standart printf fonksiyonudur. Ekrana kaç değer yazdırmak
istersek isteyelim, her defasında printf fonksiyonu ile bunu gerçekleştirebiliyoruz.
Değişken argümana
sahip fonksiyon yazarken mutlaka en az bir argüman ismi ile belirtilmelidir.
Fonksiyonun tanımlanması sırasında, bu isim başlangıç noktası olarak kullanılacaktır.
Argüman sayısının belirsiz olduğunu derleyiciye anlatmak için argüman listesinin
sonuna üç nokta (...) operatörü yazılır. (Ellipsis operatörü) İsmi ile verilen argümanlara "isimlendirilmiş
argümanlar", üç nokta ile belirtilmiş sayısı belirsiz olan argümanlara
ise "isimlendirilmemiş argümanlar" denilmektedir. Bir örnekle durumu
açıklayalım :
int topla
(int a,...)
{
//...
} |
Bu tanımlamada,
tamsayı türünde olan a argümanı isimlendirilmiş argümandır. Üç nokta ile belirtilen
diğer argümanlar ise isimlendirilmemiş argümanlardır. Bu fonksiyonu çağıracağımız
zaman parametre parantezi içerisine bir tane tamsayı türünde değişken ve sayısını
çağırma sırasında belirleyeceğimiz kadar değişken yazabiliriz. Bu tamamen programcıya
kalmıştır. Fonksiyon içerisinde belli araçlar kullanarak, fonksiyona aktarılmış
tüm parametre değişkenlerine ulaşabiliriz. Bu şekilde tanımlanmış bir fonksiyon
aşağıdaki şekillerde çağrılabilir :
{
int a = 10;
int sonuc;
//...
sonuc = topla(a,1,2,3);
sonuc = topla(a,1);
sonuc = topla(a,1,5,8,9,34); }
|
Fonksiyonun tanımlanması
sırasında argümanlara isimleriyle ulaşıyoruz. Peki bu isimlendirilmemiş argümanlara
nasıl ulaşacağız ? Bunun için öncelikle "include" önişlemci direktifi
ile "stdarg.h" başlık dosyasını kodumuza eklememiz gerekir. Bu başlık
dosyası, böyle bir fonksiyonun oluşturulması için tanımlanmış özel bir tip ve
üç tane makro içermektedir. Fonksiyon içerisinde bunları kullanarak isimlendirilmemiş
argümanlara ulaşabileceğiz. Söz konusu özel tip, çoğu derleyicide va_list ismi
ile kullanılır. Diğer üç makronun isimleri de va_arg, va_start ve va_end’dir.
ADI
|
İŞLEVİ
|
BİLDİRİMİ
|
va_list
|
va_list
tipi, va_arg ve va_end tarafından ihtiyaç duyulan bilgileri içeren bir dizidir.
Fonksiyonun başında bu tipten bir nesne tanımlanır. Bu nesneye argüman işaretçisi
diyelim. |
typedef
char *va_list |
va_start
|
va_start
makrosu ile argüman işaretçisi, fonksiyona aktarılan ilk isimlendirilmemiş
argümana konumlandırılır. Dolayısıyla işleme başlarken öncelikle bu fonksiyonu
çağırmamız gereklidir. Bu makro çağrıldıktan sonra, argüman işaretçisine
ilk değer ataması yapılmış olur. |
void
va_start(va_list vl, isimlendirilmis_arguman); |
va_arg
|
va_arg
makrosu ile eğer argüman işaretçisi fonksiyona aktarılan isimlendirilmemiş
argümanlardan ilki üzerine konumlanmışsa, isimlendirilmemiş argüman listesindeki
bir sonraki argümana ulaşım sağlanır. va_arg makrosu, argüman işaretçisinin
değerini de değiştirir. Bu şekilde adım adım ilerlenerek makro her çağrıldığında,
listedeki bir sonraki isimlendirilmemiş argümana ulaşılır. |
arguman_tipi
va_arg(va_list vl, arguman_tipi); |
va_end |
va_end
makrosu ile fonksiyondan çıkılmadan önce birtakım düzenlemelerin yapılması
sağlanır. Bu makro fonksiyon tanımlamasından çıkılmadan önce mutlaka bir
defa çağrılmalıdır. |
void
va_end(va_list vl); |
Örnek olarak çokgenlerin
çevresini hesaplayacak bir fonksiyon yazalım. Bir çokgen üç, dört, beş vb kenara
sahip olabilir. Sadece bir fonksiyon yazıp, tüm çokgenler için bu fonksiyonu
kullanarak işimizi halletmek istiyorsak, değişken sayıda argümana sahip olabilecek
bir fonksiyon yazmak iyi bir çözüm olabilir. Fonksiyonumuzun prototip bildirimini
yapalım :
double
CevreBul(int kenar_sayisi,...);
|
Görüldüğü üzere
fonksiyonumuz bir isimlendirilmiş parametreye ve isimlendirilmemiş parametrelere
sahip. (En az bir tane isimlendirilmiş parametre olması gerektiğini hatırlayınız)
Şimdi fonksiyonumuzun tanımına geçelim :
double
CevreBul(int kenar_sayisi,...)
{
va_list argList;
double arg;
double toplam = 0.0;
int i; va_start(argList,kenar_sayisi);
for (i = 0; i < kenar_sayisi; ++i)
{
arg = va_arg(argList,double);
toplam += arg;
} va_end(argList);
return toplam;
}
|
Fonksiyonun kodunu
inceleyelim. İlk olarak va_list tipinden argList isimli bir nesne tanımladık.
Bu nesne, argüman işaretçisi ismiyle anılır ve yukarıda bahsettiğimiz makrolarla
beraber kullanılarak fonksiyona aktarılan parametre değişkenlerine ulaşmamızı
sağlar. double türünde tanımladığımız arg isimli nesne, fonksiyon içerisinde
sırayla ulaşacağımız parametre değişkenlerini tutacak. toplam isimli double
türünden nesne ise, parametre değişkenlerinin toplam değerini tutacak. Fonksiyonun
ilk parametresi, çokgenin kenar sayısı bilgisini tutuyor. İlk olarak va_start
makrosunu çağırarak argüman işaretçisinin ilk isimsiz parametreye konumlanmasını
sağlıyoruz. Çevre değerini hesaplarken, kenar_sayisi parametre değişkenini döngünün
kaç kez döneceği bilgisi olarak kullanabiliriz. Döngü içerisindeki ilk satırda,
va_arg makrosu ile sıradaki parametre değişkenine ulaşıyoruz, ve bu değeri arg
nesnesinde saklıyoruz. arg nesnesinde saklanan bu değeri her adımda toplam isimli
nesneye ekliyoruz. Döngüden çıkıldığında, va_end makrosunu çağırarak gerekli
düzenlemelerin yapılmasını sağlıyoruz ve toplam nesnesinin değerine dönüyoruz.
Artık bu fonksiyonu istediğimiz sayıda kenara sahip olan bir çokgenin çevresini
bulmak için kullanabiliriz.
CevreBul(3,10.0,10.0,10.0);
//Kenar uzunlukları 10 olan üçgenin çevresi
CevreBul(4,10.0,20.0,30.0,40.0);
//Kenar uzunlukları 10, 20, 30, 40 olan çokgenin çevresi
|
Bu çağırmalar sonucunda
ekrana sırasıyla 30.0 ve 100.0 değerleri yazacaktır. Bir sonraki makalemizde görüşmek üzere...
Kaynak
kod için tıklayın.
Makale:
Farklı Sayıda Argümana Sahip Olabilen Fonksiyonlar C ve Sistem Programlama Çiğdem Çavdaroğlu
|