Возможен вызов функции с различным количеством аргументов и разных
типов. В файле
stdarg.h
определяется тип
va_list
и описываются три макроса для обработки списка аргументов
с такими значениями и таких типов, которые неизвестны вызываемой функции.
Вызванная функция должна указать на объект типа
va_list,
который используется макросами
va_start,
va_arg
и
va_end.
va_start
Макрос
va_start
должен вызываться в первую очередь, поскольку он инициализирует
ap
для последующего его использования макросами
va_arg
и
va_end.
Параметр
last
является именем последнего параметра перед переменным списком аргументов;
например, именем последнего параметра, тип которого известен вызывающей функции.
Так как адрес этого параметра может быть использован в макросе
va_start,
то он не должен быть объявлен как зарегистрированная переменная, функция или
тип массива.
va_arg
Макрос
va_arg
возвращает выражение, которое имеет такой же тип и значение, как и
следующий по вызову аргумент. Параметр
ap
является
va_listap ,
инициализированным с помощью макроса
va_start.
Каждый вызов
va_arg
изменяет
ap
так, что следующий вызов возвращает последующий аргумент. Параметр
type
является типом, определенным таким образом,
что указатель на обьект этого типа может быть получен простым добавлением * к
type.
Первый вызов макроса
va_arg
сразу после
va_start
возвращает аргумент после
last.
Последующие вызовы будут возвращать величины оставшихся аргументов.
Если нет следующего аргумента или если
type
несовместим с типом следующего аргумента, то могут возникнуть различные
ошибки.
Если
ap
передается функции, использующей
va_arg(ap,type),
то значение
ap
будет неопределено после возвращения функции.
va_end
Каждый вызов
va_start
должен сопровождаться вызовом
va_end
в той же функции. После вызова
va_end(ap)
переменная
ap
не определена. Возможны множественные пересечения списка,
обособленные скобками от
va_start
и
va_end.
va_end
может быть и функцией и макросом.
va_copy
va_list
является указателем на стек функции с различным количеством аргументов.
В таких случаях используется выражение
va_list aq = ap;
К сожалению, существуют системы, которые превращают его
в массив указателей (длиной 1), поэтому еще необходимо
va_list aq;
*aq = *ap;
И в завершении, в системах, передающих параметры
через регистры, возможно появится необходимость в
va_start
для выделения памяти, сохранению там параметров и
указателе на следующие параметры, поэтому может использоваться и
va_arg.
va_end
высвободит память обратно.
Для уравновешивания этой ситуации в C99 добавлен макрос
va_copy,
после чего вышеописанные выражения могут выглядеть так:
va_list aq;
va_copy(aq, ap);
...
va_end(aq);
Каждый вызов
va_copy
должен быть парным с вызовом
va_end
для каждой функции.
В некоторых системах вместо
va_copy
существует
__va_copy.
ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ
Функция
foo
принимает строку форматирующих символов и выводит аргументы,
ассоциированные с соответствующим символом по типу.
#include <stdio.h>
#include <stdarg.h>
void foo(char *fmt, ...) {
va_list ap;
int d;
char c, *p, *s;
va_start(ap, fmt);
while (*fmt)
switch(*fmt++) {
case 's': /* строка */
s = va_arg(ap, char *);
printf("строка %s\n", s);
break;
case 'd': /* целое */
d = va_arg(ap, int);
printf("целое %d\n", d);
break;
case 'c': /* символ */
/* необходимо указание типов, т.к. va_arg
воспринимает только полностью определенные типы */
c = (char) va_arg(ap, int);
printf("символ %c\n", c);
break;
}
va_end(ap);
}
СООТВЕТСТВИЕ СТАНДАРТАМ
Макросы
va_start,
va_arg
и
va_end
соответствуют ANSI X3.159-1989 (``C89'').
C99 определяет макрос
va_copy.
СОВМЕСТИМОСТЬ
Эти макросы
не
являются совместимыми с более ранними реализациями.
Совместимые реализации можно найти в файле заголовков
varargs.h.
СРАВНЕНИЕ
Исторически сложившийся синтаксис выглядит следующим образом:
В некоторых системах
va_end
содержит завершающий символ '}' соответствующий '{' в
va_start,
таким образом, оба макроса заключены в одной функции.
НАЙДЕННЫЕ ОШИБКИ
В отличие от макроса
varargs,
макрос
stdarg
не позволяет программистам создавать функции без фиксированного
количества аргументов. В основном проблемы возникают при
конвертировании исходных кодов с
varargs
в коды
stdarg,
а также возникают некоторые проблемы с переменными функциями,
передающимим все свои аргументы функции, принимающей аргумент
va_list
(так, как это делает, например,
vfprintf(3)).