stdarg.h - stdarg.h

stdarg.h ist ein Header in der C-Standardbibliothek der Programmiersprache C , mit dem Funktionen eine unbestimmte Anzahl von Argumenten akzeptieren können . Es bietet die Möglichkeit, eine Liste von Funktionsargumenten mit unbekannter Anzahl und unbekanntem Typ durchzugehen. C ++ bietet diese Funktionalität im Header cstdarg .

Der Inhalt von stdarg.h wird normalerweise in variadischen Funktionen verwendet , obwohl er in anderen Funktionen (zum Beispiel vprintf ) verwendet werden kann, die von variadischen Funktionen aufgerufen werden.

Variadische Funktionen deklarieren

Variadische Funktionen sind Funktionen, die eine variable Anzahl von Argumenten annehmen können und anstelle des letzten Parameters mit einem Auslassungszeichen deklariert werden. Ein Beispiel für eine solche Funktion ist printf . Eine typische Erklärung ist

int check(int a, double b, ...);

Variadische Funktionen müssen mindestens einen benannten Parameter haben, also zum Beispiel

char *wrong(...);

ist in C nicht zulässig. (In C ++ ist eine solche Deklaration zulässig.) In C muss vor den Auslassungspunkten ein Komma stehen. In C ++ ist dies optional.

Variadische Funktionen definieren

In einer Definition wird dieselbe Syntax verwendet:

long func(char, double, int, ...);

long func(char a, double b, int c, ...)
{
    /* ... */
}

In Funktionsdefinitionen alten Stils wird möglicherweise keine Ellipse angezeigt.

stdarg.h Typen

Name Beschreibung Kompatibilität
va_list Typ für iterierende Argumente C89

stdarg.h Makros

Name Beschreibung Kompatibilität
va_start Beginnen Sie mit der Iteration von Argumenten mit a va_list C89
va_arg Rufen Sie ein Argument ab C89
va_end Befreie ein va_list C89
va_copy Kopieren Sie den Inhalt von einem va_list zum anderen C99

Zugriff auf die Argumente

Um auf die unbenannten Argumente zugreifen zu können, muss va_list in der variadischen Funktion eine Variable vom Typ deklariert werden. Das Makro va_start wird dann mit zwei Argumenten aufgerufen: Das erste ist die vom Typ deklarierte Variable va_list , das zweite ist der Name des zuletzt genannten Parameters der Funktion. Danach va_arg liefert jeder Aufruf des Makros das nächste Argument. Das erste Argument va_arg ist das va_list und das zweite ist der Typ des nächsten Arguments, das an die Funktion übergeben wird. Schließlich muss das va_end Makro am aufgerufen werden, va_list bevor die Funktion zurückkehrt. (Es ist nicht erforderlich, alle Argumente einzulesen.)

C99 bietet ein zusätzliches Makro, va_copy das den Status von a duplizieren kann va_list . Der Makroaufruf va_copy(va2, va1) Kopien va1 in va2 .

Es ist kein Mechanismus zum Bestimmen der Anzahl oder der Typen der unbenannten Argumente definiert, die an die Funktion übergeben werden. Die Funktion ist lediglich erforderlich, um dies irgendwie zu wissen oder zu bestimmen, deren Mittel variieren. Zu den gängigen Konventionen gehören:

  • Verwendung einer printf oder einer scanf ähnlichen Formatzeichenfolge mit eingebetteten Bezeichnern, die Argumenttypen angeben.
  • Ein Sentinel-Wert am Ende der variadischen Argumente.
  • Ein Zählargument, das die Anzahl der variadischen Argumente angibt.

Übergeben unbenannter Argumente an andere Aufrufe

Da die Größe der Liste der unbenannten Argumente im Allgemeinen unbekannt ist (die von den meisten Compilern verwendeten Aufrufkonventionen erlauben es nicht, die Größe des unbenannten Argumentblocks zu bestimmen, auf den va_list innerhalb der Empfangsfunktion verwiesen wird ), gibt es auch keine zuverlässige generische Möglichkeit, die Liste weiterzuleiten unbenannte Argumente in eine andere variable Funktion. Selbst wenn die Bestimmung der Größe der Argumentliste auf indirekte Weise möglich ist (z. B. durch Parsen der Formatzeichenfolge von fprintf() ), gibt es keine tragbare Möglichkeit, die dynamisch bestimmte Anzahl von Argumenten als Anzahl und Größe an den inneren variadischen Aufruf zu übergeben Die Anzahl der Argumente, die an solche Aufrufe übergeben werden, muss zur Kompilierungszeit allgemein bekannt sein. Bis zu einem gewissen Grad kann diese Einschränkung gelockert werden, indem variadische Makros anstelle variadischer Funktionen verwendet werden. Darüber hinaus bieten die meisten Standardbibliotheksverfahren v alternative Versionen mit Präfix, die einen Verweis auf die Liste der unbenannten Argumente (dh eine initialisierte va_list Variable) anstelle der Liste der unbenannten Argumente selbst akzeptieren . Zum Beispiel vfprintf() ist eine alternative Version der fprintf() Erwartung einer va_list anstelle der tatsächlichen unbenannten Argumentliste. Eine benutzerdefinierte Variadic-Funktion kann daher eine va_list Variable mithilfe von initialisieren va_start und an eine geeignete Standardbibliotheksfunktion übergeben, wodurch die unbenannte Argumentliste als Referenz übergeben wird, anstatt sie als Wert auszuführen . Da es in C keine zuverlässige Möglichkeit gibt, unbenannte Argumentlisten als Wert zu übergeben, wird die Bereitstellung variabler API- Funktionen ohne die Annahme gleichwertiger Funktionen va_list als schlechte Programmierpraxis angesehen.

Typensicherheit

Einige C-Implementierungen bieten C-Erweiterungen, mit denen der Compiler die ordnungsgemäße Verwendung von Formatzeichenfolgen und Sentinels überprüfen kann. Abgesehen von diesen Erweiterungen kann der Compiler normalerweise nicht prüfen, ob die übergebenen unbenannten Argumente vom Typ sind, den die Funktion erwartet, oder sie in den erforderlichen Typ konvertieren. Daher sollte darauf geachtet werden, dass diesbezüglich die Richtigkeit gewährleistet ist, da sich ein undefiniertes Verhalten ergibt, wenn die Typen nicht übereinstimmen. Wenn beispielsweise der erwartete Typ lautet int * , sollte ein Nullzeiger als übergeben werden (int *)NULL . Das Schreiben NULL würde nur zu einem Argument vom Typ entweder int oder führen void * , von denen keines korrekt ist. Eine weitere Überlegung sind die Standardargument- Promotions, die auf die unbenannten Argumente angewendet werden. A float wird automatisch zu a befördert double . Ebenso werden Argumente von Typen, die enger als ein int sind, zu int oder befördert unsigned int . Die Funktion, die die unbenannten Argumente empfängt, muss den heraufgestuften Typ erwarten.

GCC hat eine Erweiterung, die die übergebenen Argumente überprüft:

format(archetype, string-index, first-to-check)

Das Format - Attribut gibt an, dass eine Funktion übernimmt printf , scanf , strftime oder strfmon Stil Argumente , den Typ-geprüft gegen einen Format - String sein sollte. Zum Beispiel die Erklärung:

extern int
my_printf (void *my_object, const char *my_format, ...)
      __attribute__ ((format (printf, 2, 3)));

Der Compiler überprüft die Argumente in Aufrufen auf my_printf Konsistenz mit dem printf String-Argument im Stilformat my_format .

-  "5.27 Erweiterungen der C-Sprachfamilie - Deklarieren von Funktionsattributen" . Abgerufen am 03.01.2009 .

Beispiel

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

/* print all args one at a time until a negative argument is seen;
   all args are assumed to be of int type */
void printargs(int arg1, ...)
{
  va_list ap;
  int i;

  va_start(ap, arg1); 
  for (i = arg1; i >= 0; i = va_arg(ap, int))
    printf("%d ", i);
  va_end(ap);
  putchar('\n');
}

int main(void)
{
   printargs(5, 2, 14, 84, 97, 15, -1, 48, -1);
   printargs(84, 51, -1, 3);
   printargs(-1);
   printargs(1, -1);
   return 0;
}

Dieses Programm liefert die Ausgabe:

5 2 14 84 97 15
84 51

1

Um andere var args-Funktionen innerhalb Ihrer Funktion aufzurufen (z. B. sprintf), müssen Sie die var arg-Version der Funktion verwenden (in diesem Beispiel vsprintf):

void MyPrintf(const char *format, ...)
{
  va_list args;
  char buffer[BUFSIZ];

  va_start(args, format);
  vsnprintf(buffer, sizeof buffer, format, args);
  va_end(args);
  FlushFunnyStream(buffer);
}

varargs.h

Veraltete Versionen von POSIX haben den Legacy-Header definiert varargs.h , der vor der Standardisierung von C erstellt wurde und ähnliche Funktionen bietet stdarg.h . Dieser Header ist weder Teil von ISO C noch von POSIX. Die Datei, wie sie in der zweiten Version der Single UNIX-Spezifikation definiert ist , enthält einfach alle Funktionen von C89 stdarg.h , mit den folgenden Ausnahmen:

  • Es kann nicht in Standard-C-Definitionen im neuen Stil verwendet werden
  • Das angegebene Argument kann weggelassen werden (Standard C erfordert mindestens ein Argument).

Die Schnittstelle ist auch anders. Zum printargs Beispiel würde man stattdessen schreiben:

#include <stdio.h>
#include <varargs.h>

/* There is no "void" type; use an implicit int return. */
printargs(arg1, va_alist)
  va_dcl /* no semicolon here! */
{
  va_list ap;
  int i;

  va_start(ap); 
  for (i = arg1; i >= 0; i = va_arg(ap, int))
    printf("%d ", i);
  va_end(ap);
  putchar('\n');
  return;
}

und heißt genauso.

varargs.h erfordert aufgrund der Funktionsweise der Implementierung Funktionsdefinitionen im alten Stil. Umgekehrt ist es nicht möglich, Funktionsdefinitionen alten Stils mit zu mischen stdarg.h .

Verweise