Variadic fonksiyonlar değişken sayıda argüman alabilir. Sıradan C fonksiyonlarının alacağı argüman sayısı ve türü önceden belirtilir. Her fonksiyon çağrısında, fonksiyon önceden türü(type) belirtilmiş argümanları bekler.
Ancak bazı fonksiyonlar önceden belirlenmemiş sayıda argüman alabilirler. Bunlar variadic fonksiyonlardır.
Variadic fonksiyonlara en güzel örneklerden birisi printf
fonksiyonudur. Printf fonksiyonunun
kaç tane argüman alacağı belirtilmemiştir. İstediğimiz kadar argüman gönderebiliriz.
Printf fonksiyonunun protatipi aşağıdaki gibidir.
int printf(char *fmt, ...)
Printf fonksiyonundaki kırmızı renkle gösterilmiş olan üç nokta(ellipsis), bu alana istenilen sayıda ve herhangi bir türde argümanı alabileceğimizi söylüyor.
NOT: Bir fonksiyonda ...(ellipsis) kullanırken dikkat edilmesi gerekenler şunlardır;
int printf(...) /* YANLIŞ */ int printf(char *fmt, ...) /* DOĞRU */
int printf(char *fmt, ..., int count) /* YANLIŞ */ int printf(char *fmt, int count, ...) /* DOĞRU */
Variadic bir fonksiyona gönderdiğimiz değişken sayıdaki argümanı yani ...(ellipsis) ile belirtilen alana gelen argümanların listesini almak için
sayfamıza <stdarg.h>
standart header dosyasını dahil etmemiz gerekir.
stdarg.h dosyası argüman listesini işlememiz için gerekli macroları barındırır.
Örnek olarak şöyle bir fonksiyonumuz olsun;
int sum(int count, ...)
stdarg.h içerisindeki macrolar şunlardır;
va_list ap; /* ap'yi va_list türünde argüman pointerı olarak tanımladık. */
void va_start (va_list ap, sonargumaninadi) /* Macro Prototipi */ /* Burdasonargumaninadiolarak belirtilen fonksiyonumuzda ...(ellipsis) ifadesinden önceki argümanı belirtiliyor. Yukarıda oluşturduğumuz fonksiyona uygularsak şöyle olacak */ va_start(ap, count);
type va_arg (va_list ap, type) /* Macro Prototipi */ /* Burda ap'miz yani argüman listesini işaret eden pointerımız, argüman listesindeki bir sonraki argümana işaret ettirilir. Ve bu argumanın türü(type) belirtilir. Çünkü programımız gönderilen argümanın hangi türde olduğunu bilemeyecektir. Bu sebeple fonksiyona en az 1 tane önceden tanımlanmış bir argüman belirtilir. Bu önceden tanımlanmış argümanla, ...(ellipsis) alanına gelen argümanların türü belirlenebilir. Printf örneğinde ilk argüman alanına sonraki argümanların hangi türde olacağı bilgisini giriyoruz. ÖR: printf("%s", "hello world!"); */ int sum; sum = va_arg(ap, int);
va_arg argüman pointerının(ap) bir sonraki argümana işaret ettirmesini sağlıyor. va_start ile argüman pointerımızı ...(ellipsis) alanından önceki argümanı işaret etmesi gerektiğini söylemiştik. va_arg ise ap'yi bir sonrakine işaret edecek şekilde kaydırdığı için ... alanındaki ilk argümana va_arg ile ulaşabiliriz.
Programın çalışır hali şu şekilde olacaktır:
#include <stdio.h>
#include <stdarg.h>
int sum(int count, ...); /* Prototip */
int main(void){
printf("%s%d\n","Total : ",sum(9,1,2,3,4,5,6,7,8,9));
return 0;
}
int sum(int count, ...){
va_list ap;
va_start(ap,count);
int sum = 0, i = 1;
while(i != count + 1){
sum += va_arg(ap, int);
i++;
}
va_end(ap); /* Temizleme - AP'nin işinin bittiğini belirtmek için */
return sum;
}
Yukarıdaki programda fonksiyonumuzda kırmızı ile belirtilen argüman(9) kaç tane argümanın fonksiyona iletileceği bilgisini içeriyor. Program buna göre işlem yapıyor.
Aslında bunun için doğrudan bir yol yok. Genellikle önceden tanımlanmış argümana kendisinden sonra gelecek argümanlar hakkında bilgi verilir. Argüman sayısı ya da her argümanda yapılacak işlem gibi, böylece bu tanımlanmış argümandaki işlemler bittiğinde başka argüman olmadığı anlaşılır.
Belirttiğimiz işlemi yapan fonksiyonu yazalım:
#include <stdio.h> #include <stdarg.h> void calculate(char *islem, int count, ...); int main(void){ calculate("*",3,4,5,4); calculate("+",4,10,20,30,40); calculate("-",5,50,20,4,10,2); calculate("/",6,1000,2,5,5,2,5); /* calculate fonksiyonuna istediğimiz sayıda değer girip istediğimiz işlemi yaptırabiliriz. Kırmızı ile belirtilen alanlar calculate fonksiyonuna işlem yapılmak üzere gönderilen değerlerin sayısı */ return 0; } void calculate(char *islem, int count, ...){ va_list ap; va_start(ap, count); // count'u pointerın ilk argümanı olarak başlatıyoruz int a,b,i, result = 0; a = b = i = 1; switch(*islem){ case '*': result = 1; while(i <= count){ result *= va_arg(ap, int); i++; } break; case '+': while(i <= count){ result += va_arg(ap, int); i++; } break; case '-': while(i <= count){ if(a == 1){ result = va_arg(ap, int); a = 0; } else{ result -= va_arg(ap, int); } i++; } break; case '/': while(i <= count){ if(b == 1){ result = va_arg(ap, int); b = 0; } else{ result /= va_arg(ap, int); } i++; } break; } va_end(ap); printf("%s%d\n","Calculate: ", result); }
Çıktı:
user@pc:~/c/variadic$ gcc variadic.c -Wall user@pc:~/c/variadic$ ./a.out Calculate: 80 Calculate: 100 Calculate: 14 Calculate: 2
Programmımız içerisindeki calculate fonksiyonuna hangi işlemi yapacağımızı ve kaç sayı ile işlem yapması gerektiğini bildirdik. Sonuçlara bakarsak hiçbir hata olmadan doğru hesapladığı görebiliriz.