FAQ:Variable Argumentlisten

[ FAQ in de.comp.lang.c ]


Diese FAQ bezieht sich in ihrer Gänze auf den inzwischen nicht mehr aktuellen ISO-C Standard 9899:1990, vielfach auch als C90 bezeichnet. Der seit Dezember 1999 existierende neue ISO 9899:1999 Standard (oder auch C99) wird nicht berücksichtigt.


[ Inhalt ][ Index ][ ZurÜck ][ Weiter ]


Frage 7.1: Wie kann ich eine Funktion schreiben, die eine variable Anzahl von Argumenten übergeben bekommt?

Antwort: Unter Benutzung der Macros in <stdarg.h> (oder notfalls unter Benutzung des älteren Files <varargs.h>).

Hier ist als Beispiel eine Funktion, die eine beliebige Anzahl von Strings in einem neu belegten Speicherbereich aneinanderhängt:

	    #include <stdlib.h>             /* malloc, NULL, size_t */
	    #include <stdarg.h>             /* va_ Zeugs */
	    #include <string.h>             /* strcat und Konsorten */

	    char *vstrcat(char *first, ...)
	    {
	    size_t len = 0;
	    char *retbuf;
	    va_list argp;
	    char *p;

	    if(first == NULL)
		    return NULL;

	    len = strlen(first);

	    va_start(argp, first);

	    while((p = va_arg(argp, char *)) != NULL)
		    len += strlen(p);

	    va_end(argp);

	    retbuf = malloc(len + 1);   /* +1 for abschliessende 0 */

	    if(retbuf == NULL)
		    return NULL;        /* Fehler */

	    (void)strcpy(retbuf, first);

	    va_start(argp, first);

	    while((p = va_arg(argp, char *)) != NULL)
		    (void)strcat(retbuf, p);

	    va_end(argp);

	    return retbuf;
	    }

Benutzt wird die Funktion z.B. so:

	    char *str = vstrcat("Hello, ", "world!", (char *)NULL);

Achtung: Der Cast für das letzte Argument ist notwendig (siehe Frage 1.2). Außerdem muß die aufrufende Funktion den von vstrcat belegten Speicher wieder freigegen!

Für einen Vor-ANSI Compiler sollte keine Prototypendefinition erfolgen ("char *vstrcat(first) char *first; {"), anstatt von <stdlib.h> sollte <stdio.h> eingebunden werden, malloc sollte "zu Fuß" als "extern char* malloc();" deklariert werden, und statt size_t ist int zu verwenden. Eventuell müssen auch die (void) Casts entfernt und varargs.h anstatt von stdargs.h verwendet werden. Einige Hinweise dazu werden in der Antwort auf die nächste Frage gegeben.

Bei Funktionen mit variablen Argumentlisten stellt ein Prototyp keine Informationen über die variablen Argumente bereit, der Compiler wendet deshalb für diese Argumente die "default promotions" an (siehe Frage 5.8), aus dem Grund müssen Null-Zeiger Argumente auch explizit gecastet werden (Frage 1.2).

References: K&R II Sec. 7.3 p. 155, Sec. B7 p. 254; H&S Sec. 13.4 pp. 286-9; ANSI Secs. 4.8 through 4.8.1.3 .


Frage 7.2: Wie kann ich eine Funktion schreiben, die einen Format-String und eine variable Anzahl von Argumenten nimmt (ähnlich wie printf) und die diese Argumente an printf weitergibt?

Antwort: Das ist möglich unter Verwendung von vprintf, vfprintf oder vsprintf.

Hier ist eine "error" Funktion, die eine Fehlermeldung ausgibt, wobei dem "error" vorangestellt und die Zeile mit einem Newline abgeschlossen wird:

	#include <stdio.h>
	#include <stdarg.h>

	void
	error(char *fmt, ...)
	{
		va_list argp;
		fprintf(stderr, "error: ");
		va_start(argp, fmt);
		vfprintf(stderr, fmt, argp);
		va_end(argp);
		fprintf(stderr, "\n");
	}

Wenn das ältere <varargs.h> Headerfile verwendet werden muß, dann muß der Header der Funktion wie folgt geändert werden:

	void error(va_alist)
	va_dcl
	{
		char *fmt;

Der Aufruf von va_start sieht dann so aus:

	va_start(argp);

Und zwischen den Aufrufen von va_start und vfprintf muß noch die folgende Zeile eingefügt werden:

	fmt = va_arg(argp, char *);

(Achtung: Nach va_dcl darf kein Semikolon stehen!)

References: K&R II Sec. 8.3 p. 174, Sec. B1.2 p. 245; H&S Sec. 17.12 p. 337; ANSI Secs. 4.9.6.7, 4.9.6.8, 4.9.6.9 .


Frage 7.3: Wie kann ich zur Laufzeit feststellen, mit wievielen Argumenten eine Funktion aufgerufen wurde?

Antwort: Diese Information ist auf portablem Wege nicht erhältlich. Einige ältere Systeme verfügen über eine (nicht standardkonforme) nargs() Funktion, aber deren Nützlichkeit war schon immer fraglich, da sie die Anzahl der übergebenen Worte zurückgegeben hat, und nicht die Anzahl der Argumente (structs und Gleitkommawerte benötigen bei der Übergabe als Parameter üblicherweise mehr als ein Wort).

Jede Funktion, der eine variable Anzahl von Argumenten übergeben werden kann, muß in der Lage sein, die Anzahl der Argumente selber zu ermitteln. Funktionen wie printf entnehmen diese Information den Formatangaben (%d, %i usw.), die sich im Formatstring befinden, der als erstes Argument übergeben wird (das ist auch der Grund, warum man Laufzeitfehler bekommt, wenn der Formatstring und die wirklich übergebenen Parameter nicht übereinstimmen). Eine andere gebräuchliche Technik (die vor allem dann nützlich ist, wenn alle übergebenen Argumente vom selben Typ sind) ist das Einfügen eines speziellen Ende-Symbols am Schluß der Liste (oft wird 0 oder -1 verwendet oder ein passend gecasteter Null-Zeiger). Beispiele für diese Techniken werden in den Antworten zu Fragen 1.2 und 7.1 gezeigt.


Frage 7.4: Ich kann den va_arg Macro nicht dazu bringen, ein Argument vom Typ "Zeiger auf eine Funktion" korrekt zu verwenden.

Antwort: Der Makro va_arg verwendet üblicherweise diverse Typ-Umwandlungen um seine Arbeit durchzuführen. Diese Typ-Umwandlungen funktionieren teilweise nicht korrekt, wenn der übergebene Datentyp relativ komplex ist (wie ein Zeiger auf eine Funktion). Abhilfe ist möglich durch Verwendung eines typedefs für den Funktionszeiger.

References: ANSI Sec. 4.8.1.2 p. 124.


Frage 7.5: Wie kann ich eine Funktion schreiben, die eine variable Anzahl von Argumenten nimmt und diese Argumente an eine andere Funktion weitergeben (die auch eine variable Anzahl von Argumenten nimmt).

Antwort: Dafür gibt es keine allgemeine Lösung. Die zweite Funktion muß einen va_list Zeiger als Argument nehmen, wie z.B. vfprintf. Wenn die Argumente an die zweite Funktion als echte Argumente übergeben werden (und nicht indirekt über einen va_list Zeiger), dann gibt es keine portable Lösung. Das Problem ist in Assembler lösbar, aber das ist selbstverständlich nicht mehr portabel.


Frage 7.6: Wie kann ich eine Funktion mit einer zur Laufzeit erzeugten, variablen Argumentliste aufrufen?

Antwort: Dieses Problem ist nicht portabel lösbar. Wer besonders neugierig ist, der kann denn Maintainer der (englischen) FAQ (scs@eskimo.com) fragen, der offensichtlich ein paar Ideen zu diesem Thema hat... (Siehe auch Frage 16.11).

[ Inhalt ][ Index ][ ZurÜck ][ Weiter ]


[ FAQ Logo ]   © 1997-2004 Jochen Schoof (joscho@bigfoot.de)
Diese Version wurde am 14. März 2004 erzeugt. Sie wird zukünftig nicht weiter gepflegt.