Değişkin İşlevler
Önceki Ek A. Kütüphanedeki C Dili Oluşumları Sonraki
Değişkin İşlevler
ISO C, değişen sayıda argüman alabilecek bir işlevin bildirilebilmesi için bir sözdizimi tanımlamıştır. Buna rağmen, dilin kendisi bu tür işlevlerin gerekli olmayan argümanlarına erişim için bir mekanizma sağlamaz; bu nedenle, stdarg.h başlık dosyasındaki makrolarla tanımlanmış olan değişken argümanları kullanacaksınız.
Bu tür işlevler, değişken argümanlı işlevler [varargs functions] ya da değişkin işlevler [variadic functions] adını alır.
Bu bölümde değişkin işlevlerin nasıl bildirildikleri, nasıl yazıldıkları ve nasıl çağrıldıkları anlatılmıştır.
Uyumluluk Bilgisi:
Birçok eski C gerçeklemesi, stdarg.h kullanılarak değişken sayıda argümanla işlevleri tanımlama mekanizmasına benzeyen ama uyumlu olmayan bir mekanizma sağlar.
Değişkin İşlevler Neden Kullanılır
Sıradan C işlevleri sabit sayıda argüman alır. Bir işlevi tanımlarken her argüman için veri türünü de belirtirsiniz. Her işlev çağrısında istenen sayıda argüman verirken argümanlarını da gerekirse tür dönüşümü yaparak istenen türde sağlarsınız. Bu noktada, örneğin, foo işlevi int foo (int, char *); deyimi ile bildirilmişse, onu iki argümanla; bir sayı ve bir dizge göstericisi ile çağırmalısınız.
Fakat bazı işlevler sınırsız sayıda argümanı anlamlı olarak kabul ederek işlemler uygulayabilir.
Bazı durumlarda bir işlev çok sayıda değeri bir blok olarak işleyerek elde edebilir. Örneğin, varsayalım ki, bir işlev belirli sayıda değeri tutmak için malloc ile bir tek boyutlu dizi ayırsın. Dizinin uzunluğuna bağlı sayıda değer üzerinde işlem yapılabileceğinden değişen sayıda argümanlı oluşumlar olmaksızın, her olası dizi uzunluğu için ayrı bir işlev tanımlamak zorunda kalırsınız.
Kütüphane işlevi printf (Bkz. Biçimli Çıktı) değişken argümanlı başka bir işlev sınıfından bir örnek olarak verilebilir. Bu işlev bir biçim şablonu dizgesi altında (değişken sayıda ve türdeki) argümanlarını basar.
Bunlar, argüman sayısı çağrı sırasında seçilen bir değişkin işlev tanımlamak için iyi sebeplerdir.
open gibi bazı işlevler sabit sayıda argüman almasına rağmen arasıra son birkaçını yoksayar. ISO C'ye kesin bağlılık durumunda bu işlevlerin değişkin olarak tanımlanması gerekir; uygulamada ise, GNU C derleyicisi ve diğer bir çok C derleyicisi böyle bir işlevi sabit sayıda argümanla tanımlamayı ve sadece bildirme işlemini yaparken işlevi değişkin olarak bildirerek (ya da argümanlarının tümünü bildirmeyerek) yapmayı tercih eder.
Değişkin İşlevler Nasıl Tanımlanır ve Kullanılır
Bir değişkin işlevin tanımlanması ve kullanılması üç adımdan oluşur:
Tanım
Bir işlevin değişkin olarak tanımlanması, argüman listesinde bir üçlü nokta () kullanarak ve değişken sayıda argümana erişmek için özel makrolar kullanılarak yapılır. Bkz. Argüman değerlerinin Alınması.
Bildirim
Bir işlevin değişkin olarak bildirilmesi, onu çağıran tüm dosyalarda bir üçlü nokta () içeren bir prototip kullanarak yapılır. Bkz. Değişen Sayıda Argüman için Sözdizimi.
Çağrı
İşlev çağrısı sabit agümanlara ek olarak değişken sayıdaki argümanları yazarak yapılır. Bkz. Değişkin İşlevlerin Çağrılması.
Değişen Sayıda Argüman için Sözdizimi
Değişken sayıda argüman kabul eden bir işlev şimdi açıklanacağı gibi bir prototiple bildirilmelidir. Önce sabit argümanları yazacaksınız, ardından da ek argümanlar olduğunu belirtmek üzere bir üçlü nokta () gelecek. ISO C sözdizimi üçlü noktadan önce en az bir sabit argüman gerektirir. Örnek:
int
func (const char *a, int b, …)
{
  …
}
Bu örnekte, int türünde bir dönüş değeri olan ve biri const char * türünde diğeri int türünde iki argüman gerektiren func işlevi tanımlanmıştır. Ayrıca işlevin gerekli olan iki argümandan başka belirsiz sayıda anonim argümanları da vardır.
Uyumluluk Bilgisi:
Bazı C derleyicileri için son gerekli argümanın işlev tanımında register olarak bildirilmemesi gereklidir. Bundan başka, bu argümanın türü kendinden terfili olmalıdır: Şöyleki, öntanımlı terfiler onun türünü değiştirmemelidir. Bu kurallar dizi ve işlev türleri ile float, char (signed ya da değil) ve short int (signed ya da değil) türleri dışarda tutar. Bu aslında bir ISO C gerekliliğidir.
Argüman değerlerinin Alınması
Gerekli olan argümanların isimleri vardır ve bu isimleri kullanarak onları değerlerine erişirsiniz. Ama isteğe bağlı argümanların isimleri yoktur, çünkü onlar bir üçlü nokta ile ifade edilmiştir. O halde bu argümanlara nasıl erişilecek?
Onlara erişmenin tek yolu onlara yazıldıkları sırayla erişmektir. Bunun için stdarg.h başlık dosyasındaki özel makroları aşağıdaki üç adımlık işlemlerle kullanmalısınız:
  1. va_start kullanarak va_list türünde bir argüman gösterici değişkenini ilklendirin. Argüman gösterici, ilklendirildiğinde ilk isteğe bağlı argümanı gösterecektir.
  2. İsteğe bağlı argümanlara va_arg çağırarak erişirsiniz. İlk va_arg çağrısı ilk isteğe bağlı argümanı, ikinci çağrı ikincisini, böyle gider.
    Bu çağrı işlemini kalan isteğe bağlı argümanları yoksayacağınız yere kadar sürdürebilirsiniz. Bir işlevin argümanlarından daha azına erişmek bir sorun çıkarmaz ama daha fazla sayıda argümana erişmeye çalışırsanız bozuk değerler alırsınız.
  3. Argüman gösterici ile işiniz bittiğinde bunu va_end çağrısıyla belirtin.
    (Uygulamada, birçok C derleyicisi va_end çağrısında hiçbir şey yapmaz. Bu GNU C derleyicisi için de geçerlidir. Ancak, yazılımınızın bir gün bu çağrıyı gerektiren bir derleyici ile derlenebileceğini gözönüne alarak yine de va_end çağrısını yaparsanız iyi olur.)
va_start,va_arg ve va_end tanımları için Argümana Erişim Makroları bölümüne bakınız.
1 den 3 e kadar adımlar isteğe bağlı argümanları kabul eden işlevin içinde uygulanmalıdır. Buna rağmen va_list değişkenini bir argüman olarak başka bir işleve aktararak 2. adımı ya da tamamını burada uygulayabilirsiniz.
Bu üç adımlık işlemi tek bir işlevi defalarca çağırarak da uygulayabilirsiniz. İsteğe bağlı argümanları yoksaymak istediğinizde ise bu adımları sıfır kere uygulayabilirsiniz.
İsterseniz, birden fazla argüman gösterici değişkeniniz olabilir ve bu değişkenlerin her birini istediğiniz zaman va_start çağrılarıyla ayrı ayrı ilklendirebilirsiniz. Her argüman gösterici ile istediğiniz kadar isteğe bağlı argümanı alabilirsiniz. Her argüman gösterici değişkeni daima aynı argüman kümesine ama kendi alanında sahip olacaktır.
Taşınabilirlik Bilgisi:
Bazı derleyicilerle, bir argüman gösterici değerini bir alt işleve aktardıktan sonra, alt işlev döndüğünde aynı argüman gösterici değerini kullanımda tutmamalısınız. Tam taşınabilirlik için, onu va_end'e aktarmalısınız. Bu aslında bir ISO C gerekliliği olmakla birlikte birçok ANSI C derleyicisi ile de sorunsuz çalışır.
Aktarılan Argümanların Sayısı
Bir işleve aktarılan isteğe bağlı argümanların sayısını ve türünü saptamak için genel bir yol yoktur. Yani işlevi tasarlayan her kim ise, genellikle çağrıcı tarafından kullanılmak üzere argümanların sayısının ve türünün belirtileceği bir yol da tasarlamalıdır. Bu kimse siz olduğunuza göre her değişkin işlev için bir çağrı yöntemi tespit edip, çağrıları yazarken bunları uygulamalısınız.
Çağrı yöntemlerinden biri, isteğe bağlı argümanların sayısını sabit argümanlardan birinde belirtmektir. Bu yöntem sadece tüm isteğe bağlı argümanlar aynı türde ise çalışır.
Bir diğer çağrı yöntemi ise, sabit argümanlardan birinin bir isteğe bağlı argümanın sağlayabileceği her olası amaç için bir bit olmak üzere bir bit maskesi içermesidir. Bu bitleri önceden tanımlanmış bir sırayla sınayarak; eğer bit bir ise sonraki argümanın değerini alırsınız, değilse bir öntanımlı değer kullanırsınız.
Bir sabit argüman hem argüman sayısını hem de türünü belirten bir kalıp olarak kullanılabilir. printf işlevinin biçim dizgesi argümanı buna bir örnektir (Bkz. Biçimli Çıktı İşlevleri).
Diğer bir olasılık da, son isteğe bağlı argüman olarak bir "son belirten" değer aktarmaktır. Örneğin isteğe bağlı argümanları göstericilerden oluşan bir işleve sonuncu argüman olarak bir boş gösterici verilebilir. Örneğin, execl işlevi bu yöntemi kullanır. Bkz. Bir Dosyanın Çalıştırılması.
Değişkin İşlevlerin Çağrılması
Bir değişkin işlev çağrısına özel hiçbir şey yoktur. Parantez içine önce gerekli sonra da isteğe bağlı argümanları virgüllerle ayırarak yazarsınız. Ama önce işlevi bir prototiple bildirmeniz gerekir, böylece argüman değerlerinin nasıl dönüştürüleceğini bilirsiniz.
Prensip olarak, değişkin olarak tanımlanan işlevler çağrılmadan önce bir işlev prototipi kullanarak değişkin olarak bildirilmelidir. Bazı C derleyicileri, işlevin aldığı sabit ve değişken sayıdaki argümanlara bağlı olarak bir işleve aktarılacak aynı argüman değerleri kümesi için farklı çağrı yöntemleri kullandığı için bu böyledir.
Uygulamada, GNU C derleyici argümanların gerekli mi, isteğe bağlı mı olduğuna bakmaksızın bir verilmiş argüman türleri kümesini hep aynı yolla aktarır. Yani argüman türleri kendiden terfili olduğu sürece onların bildirilmesini rahatça ihmal edebilirsiniz. Genellikle, değişkin işlevlerin argüman türlerini ve hatta tüm işlevleri bildirmek iyi bir fikirdir. Ancak çok kullanışlı bir kaç işlev vardır ki değişkin olarak bildirilmez; örneğin, open ve printf.
İşlev prototipinde isteğe bağlı argümanların türleri belirtilmediğinden, bir değişkin işlev çağrısında isteğe bağlı argüman değerleri üzerinde öntanımlı argüman terfileri uygulanır. Yani, nesne türleri char veya short int (signed ya da değil) ise ya int ya da unsigned int türüne, nesne türü float ise double türüne terfi ettirilir. Böylece örneğin, çağrı sırasına char türünde belirtilen bir argüman, int türüne terfi ettirilerek, işlev argümana va_arg (arg_gstr, int) ile erişebilir.
Gerekli argümanların dönüşümleri ise işlev prototipi tarafından genel bir yolla denetlenir: argüman işlev prototipinde bildirilen türde değilse, prototipte bildirilen türe dönüştürülür.
Argümana Erişim Makroları
Burada isteğe bağlı argümanlara erişmek için kullanılan makrolar açıklanmıştır. Bu makrolar stdarg.h başlık dosyasında tanımlıdır.
va_list
veri türü
va_list türü argüman gösterici değişkeni için kullanılır.
void va_start
(va_list arglist-gstr, gerekli-son-arg) 
makro
Bu makro, kullanıldığı işlevin ilk isteğe bağlı argümanını gösterecek olan arglist_gstr argüman gösterici değişkenini ilklendirir. gerekli-son-arg işlevdeki gerekli son argüman olmalıdır.
Bu makronun bir alternatifi olarak varargs.h başlık dosyasında tanımlanmış olan va_start makrosu için Eski Moda Değişkin İşlevler bölümüne bakınız.
tür va_arg
(va_list arglist-gstr, tür)
makro
Bu makro sonraki isteğe bağlı argümanın değeri ile döner ve arglist_gstr değişkeninin değerini sonraki argümanı gösterecek şekilde değiştirir. Böylece her va_arg kullanımında sırayla bir isteğe bağlı argümanın değeri alınır.
va_arg tarafından döndürülen değerin türü çağrı sırasında tür ile belirtilir. tür argümanın türü ile eşleşen kendinden terfili bir tür olmalıdır (char, short int veya float değil).
void va_end
(va_list arglist-gstr)
makro
Bu makro arglist_gstr kullanımını sonlandırır. Bir va_end çağrısından sonraki va_arg çağrıları bu arg_gstr ile çalışamaz. Aynı arglist_gstr argümanı için bir va_start çağrısı yapmadan önce bir va_end çağrısı yapılmalıdır.
GNU C kütüphanesinde, va_end hiçbir şey yapmaz ve taşınabilirlik sebebi dışında kulanmanız gerekmez.
Kimi zaman parametre listesini defalarca çözümlemeniz gerekir ya da argümanlardan birinin listedeki konumunu hatırlamak istersiniz. Bu durumda, o anki argüman değerinin bir kopyasını yaparsınız. Ancak va_list şeffaf bir tür değildir ve va_list türünden bir değişkenin değeri başka bir değişkene atanamaz (bazı türleri sadece makrolar oluşturabilir).
void __va_copy
(va_list hedef,
 va_list kaynak)
makro
Bu makro, va_list türünden nesnelerin bir bütünleyen tür olmasa bile kopyalanmasını mümkün kılar. hedef argüman göstericisi, kaynak göstericisi ile aynı argümanı göstermek üzere ilklendirilir.
Bu makro bir GNU oluşumudur ve ISO C standardının gelecek güncellemesinde ayrıca kullanılabilir olacağı umulmaktadır.
__va_copy makrosunu kullanmak isterseniz, bu makronun kullanılabilir olmayabileceği durumlara karşı hazırlıklı olmalısınız. Basit atamanın geçersiz olduğu mimarilerde __va_copy makrosunun bulunmakta olduğunu umarak yazılımınızda bu makroyu daima şöyle yazmalısınız:
{
  va_list ap, save;
  …
#ifdef __va_copy
  __va_copy (save, ap);
#else
  save = ap;
#endif
  …
}
Bir Değişkin İşlev Örneği
Burada değişken sayıda argüman kabul eden bir işlevle ilgili tam bir örneğe yer verilmiştir. İşlevin ilk argümanı diğer argümanların sayısını içermektedir. İşlev anlamsız olsa da değişken argüman oluşumunun kullanımı hakkında fikir vermek için yeterlidir.
#include <stdarg.h>
#include <stdio.h>

int
topla (int miktar,...)
{
  va_list ag;
  int i, toplam;

  va_start (ag, miktar);         /* Argüman listesi ilklendiriliyor. */

  toplam = 0;
  for (i = 0; i < miktar; i++)
    toplam += va_arg (ag, int);    /* Sonraki argümanın değeri alınıyor. */

  va_end (ag);                  /* Temizlik. */
  return toplam;
}

int
main (void)
{
  /* Bu çağrı 16 basar. */
  printf ("%d\n", topla (3, 5, 5, 6));

  /* Bu çağrı 55 basar. */
  printf ("%d\n", topla (10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));

  return 0;
}
Eski Moda Değişkin İşlevler
ISO C öncesi çağlarda yazılımcılar değişkin işlevleri yazmak için az çok farklı bir oluşum kullandılar. GNU C derleyicisi hala onu desteklemektedir; şu anda, ISO C için destek hala evrensel olmadığından, ISO C oluşumundan daha taşınabilir durumdadır. Bu eski moda değişkin işlevler oluşumu varargs.h başlık dosyasında tanımlanmıştır.
varargs.h kullanımı stdarg.h kullanımı ile hemen hemen aynıdır. (Bkz. Değişkin İşlevlerin Çağrılması) Değişkin işlevlerin çağrılması açısından bir fark yoktur. Tek fark onların nasıl tanımlandığı ile igilidir. Herşeyden önce, eski moda prototipsiz sözdizimini şöyle kullanmalısınız:
tree
build (va_alist)
     va_dcl
{
İkinci olarak, va_start'ı tek bir argüman ile vermelisiniz, bunun gibi:
  va_list p;
  va_start (p);
Eski moda değişkin işlevleri tanımlamak için kullanılan özel makrolar şunlardır:
va_alist
makro
Bu makro bir değişkin işlevdeki gerekli argüman isimlerinin listesi için kullanılır.
va_dcl
makro
Bu makro bir değişkin işlev için dolaylı argüman ya da argümanları bildirir.
void va_start
(va_list arglist-gstr)
makro
Bu makro varargs.h dosyasında tanımlanmış olarak, kullanıldığı işlevin ilk argümanını gösteren arg_gstr argüman gösterici değişkenini ilklendirir.
Diğer argüman makroları, va_arg ve va_end için varargs.h ile stdarg.h kullanımları arasında fark yoktur; ayrıntılar için Argümana Erişim Makroları bölümüne bakınız.
varargs.h ile stdarg.h dosyaları aynı derleme altında birlikte kullanılamazlar, çünkü va_start tanımları isimleri dışında birbiriyle aynı değildir.
Önceki Üst Ana Başlık Sonraki
Dahilî Kararlılığın Doğrudan Denetlenmesi Başlangıç Boş Gösterici Sabiti
Bir Linux Kitaplığı Sayfası