|
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.
|