Advanced   Java   Services varargs


varargs

Die im Header <stdarg.h> bereitgestellten Macros ermöglichen es mit wenig Aufwand Funktionen mit beliebig vielen Parametern zu schreiben. Bekannte Funktionen wie int printf(const char* format, ...); oder int scanf ( const char * format, ... ); verwenden variable Argumentenlisten. Die drei Punkte nach dem Komma, auch Ellipse genannt, leiten die variable Argumentenliste ein. Bei beiden Funktionen fällt auf, daß die variable Liste am Ende der festen Argumentenliste steht. Das ist kein Zufall, sondern muß so sein. ISO C verlangt zusätzlich ein Argument mit einem Namen vor der Ellipse. Das Argument vor der Ellipse hat die Aufgabe, der Funktion die Länge der variablen Liste mitzuteilen, damit die übergebenen Parameterwerte lorrekt auf den Stack der Funktion ghelegt werden können. Bei printf etwa entspricht die Länge der Liste der Anzahl der Prozentzeichen im Formatstring. Ein Beispiel verdeutlicht das prinzipielle Vorgehen.


Die Makros von stdarg.h

Der Header <stdarg.h> stellt eine Reihe von Makros bereit, die praktisch wie Pointer bzw. Funktionen verwendet werden können.



MakroBedeutung
va_listTypmakro hinter dem ein Voidpointer steht, der auf die erste Adresse nach dem letzten festen Argument auf dem Stack der Funktion zeigt. Nach dem Casten auf den richtigen Typ kann man Variablen dieses Typs dereferenzieren.
va_start Funktionsmakro, das zwei Parameter erhält. Der erste Parameter ist ein Pointer vom Typ va_list, mit dem zweiten Parameter übergibt man die Länge der variablen Parameterliste. Mit diesen beiden Parametern kann va_start das Makro va_list mit dem richtigen Stackpointer initialisieren.
va_argFunktionsmakro, das zwei Parameter erhält. Der erste Parameter ist ein Pointer vom Typ va_list, mit dem zweiten Parameter übergibt man den Typ der Argumente der variablen Parameterliste. Der Typ wird gebraucht um den Pointer zu casten und zu inkrementieren.
va_endFunktionsmakro, dem ein Pointer vom Typ va_list übergeben wird. Das Makro gibt Speicher frei, der während des Durchlaufs der Argumentenliste gebraucht wurde.
va_copyFunktionsmakro, das zwei Parameter erhält. Der Wert des zweiten Pointers wird in den ersten Pointer kopiert. Damit kann man die Liste (oder einenn Teil davon) ein zweites Mal durchlaufen.

Die aktuellen Implementierung dieser Makros hängt vom verwendeten Compiler ab, da die Compiler den Stack unterschiedlich verwalten dürfen.


Beispiel

Die Funktion mittelwert() ermittelt den Mittelwert einer beliebig langen Reihe von double-Zahlen.

double mittelwert(int size, ...)
{
   int i;
   double sum = 0;
   va_list sp;  // Preprozessormacro definiert einen stackpointer (nicht initialisiert)

   va_start(sp, size);
   // initialisiert mit Hilfe des letzten festen Parameters den Stackpointer sp
   printf("*sp = %f\n", *((double*)sp) );

   for(i=0; i<size; i++)
   {
      sum += va_arg(sp, double); //Requires the type to cast to. Increments sp to the next argument.
      printf("*sp = %f\n", *((double*)sp) );
   }

   va_end(sp);  // gibt Ressourcen frei
   return sum/size;
}

Ein Aufruf der Form

int main(void)
{
   puts("varargs");

   // keine automatische Konvertierung von int nach double !!
   double erg = mittelwert(7, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0);
   // falls es mehr Argumente sind werden sie ignoriert
   printf("average = %f\n", erg);

   return 0;
}

ergibt

varargs
*sp = 1.000000
*sp = 2.000000
*sp = 3.000000
*sp = 4.000000
*sp = 5.000000
*sp = 6.000000
*sp = 7.000000
average = 4.000000