stdarg.h
— заголовок в стандартной библиотеке C языка программирования C , который позволяет функциям принимать неопределенное количество аргументов . [1] Он предоставляет возможности для пошагового прохождения списка аргументов функции неизвестного количества и типа. C++ предоставляет эту функциональность в заголовке cstdarg
.
Содержимое stdarg.h
обычно используется в вариативных функциях , хотя оно может использоваться и в других функциях (например, vprintf
), вызываемых вариативными функциями.
Функции с переменным числом аргументов — это функции, которые могут принимать переменное число аргументов и объявляются с многоточием вместо последнего параметра. Примером такой функции является printf
. Типичное объявление:
проверка int ( int a , double b , ...);
Вариативные функции должны иметь по крайней мере один именованный параметр, например,
символ * неправильный (...);
не допускается в C17 и более ранних версиях, но в C++ и C23 [2] такое объявление допускается.
В языке C запятая должна предшествовать многоточию, если указан именованный параметр, тогда как в C++ это необязательно.
Тот же синтаксис используется в определении:
длинная функция ( char , double , int , ...); длинная функция ( char a , double b , int c , ...) { /* ... */ }
В определениях функций старого стиля многоточие может отсутствовать.
Согласно стандарту, доступ к неименованным аргументам можно получить через переменную типа va_list
в вариативной функции, при этом макрос va_start
также предоставляется в качестве последнего именованного параметра функции. В C23 второй аргумент является необязательным и не будет оцениваться. [2]
После этого каждый вызов макроса va_arg
возвращает следующий аргумент. Первый аргумент — va_arg
это va_list
, а второй — тип следующего аргумента, переданного функции. В качестве последнего шага макрос va_end
должен быть вызван для , va_list
прежде чем функция вернет значение. Обратите внимание, что не требуется считывать все аргументы.
C99 предоставляет дополнительный макрос, va_copy
, который может дублировать состояние va_list
. Вызов макроса va_copy(va2, va1)
копирует va1
в va2
.
Не существует определенного метода для подсчета или классификации неименованных аргументов, переданных вариативной функции. Функция должна просто определить это каким-то образом, средства чего меняются. Общие соглашения включают:
printf
«или scanf
» со встроенными спецификаторами, указывающими типы аргументов.Поскольку размер неименованного списка аргументов, как правило, неизвестен, соглашения о вызовах, используемые большинством компиляторов, не позволяют определить размер неименованного блока аргументов, на который указывает va_list
внутри принимающей функции. В результате также нет надежного, универсального способа переслать неименованные аргументы в другую вариативную функцию. Даже если определение размера списка аргументов возможно косвенными способами (например, путем анализа строки формата fprintf()
), нет переносимого способа передать динамически определенное количество аргументов во внутренний вариативный вызов, поскольку количество и размер аргументов, передаваемых в такие вызовы, как правило, должны быть известны во время компиляции. В некоторой степени это ограничение можно смягчить, используя вариативные макросы вместо вариативных функций. Кроме того, большинство стандартных библиотечных процедур предоставляют v
альтернативные версии с префиксом -, которые принимают ссылку на неименованный список аргументов (т. е. инициализированную va_list
переменную) вместо самого неименованного списка аргументов. Например, vfprintf()
является альтернативной версией fprintf()
ожидания a va_list
вместо фактического неименованного списка аргументов. Таким образом, определяемая пользователем вариативная функция может инициализировать va_list
переменную, используя va_start
ее, и передать ее в соответствующую стандартную библиотечную функцию, фактически передавая неименованный список аргументов по ссылке, а не по значению. Поскольку в C нет надежного способа передавать неименованные списки аргументов по значению, предоставление вариативных функций API без предоставления эквивалентных функций, принимающих va_list
вместо этого, считается плохой практикой программирования.
Некоторые реализации C предоставляют расширения C, которые позволяют компилятору проверять правильность использования строк формата и сигнальных символов. За исключением этих расширений, компилятор обычно не может проверить, соответствуют ли переданные неименованные аргументы типу, ожидаемому функцией, или преобразовать их в требуемый тип. Поэтому следует проявлять осторожность, чтобы обеспечить корректность в этом отношении, поскольку неопределенное поведение приводит к несовпадению типов. Например, если ожидаемый тип — int *
, то нулевой указатель должен быть передан как (int *)NULL
. Запись just NULL
приведет к аргументу типа либо int
или , ни один из которых не является правильным. Еще одно соображение — это повышенияvoid *
аргументов по умолчанию, применяемые к неименованным аргументам. A будет автоматически повышен до a . Аналогично, аргументы типов, более узких, чем an , будут повышены до или . Функция, получающая неименованные аргументы, должна ожидать повышенный тип.float
double
int
int
unsigned int
GCC имеет расширение, которое проверяет переданные аргументы:
format(archetype, string-index, first-to-check)
Атрибут format указывает, что функция принимает аргументы
printf
,scanf
,strftime
илиstrfmon
style, которые должны быть проверены по типу относительно строки формата. Например, объявление:extern int my_printf ( void * my_object , const char * my_format , ...) __attribute__ (( format ( printf , 2 , 3 )));заставляет компилятор проверять аргументы в вызовах на
my_printf
соответствиеprintf
формату стиля string argumentmy_format
.— "5.27 Расширения семейства языков C — объявление атрибутов функций" . Получено 03.01.2009 .
#include <stdio.h> #include <stdarg.h> /* вывести все аргументы по одному, пока не будет виден отрицательный аргумент; предполагается, что все аргументы имеют тип int */ 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 ) { аргументы_печати ( 5 , 2 , 14 , 84 , 97 , 15 , -1 , 48 , -1 ); аргументы_печати ( 84 , 51 , -1 , 3 ); аргументы_печати ( -1 ); аргументы_печати ( 1 , -1 ); return 0 ; }
Эта программа выдает результат:
5 2 14 84 97 1584 511
Чтобы вызвать другие функции var args из вашей функции (например, sprintf), вам необходимо использовать версию var arg функции (в этом примере vsprintf):
void MyPrintf ( const char * format , ...) { va_list args ; буфер символов [ BUFSIZ ]; va_start ( args , format ); vsnprintf ( buffer , sizeof buffer , format , args ); va_end ( args ); FlushFunnyStream ( buffer ); }
Устаревшие версии POSIX определили устаревший заголовок varargs.h
, который датируется периодом до стандартизации C и обеспечивает функциональность, аналогичную stdarg.h
. Этот заголовок не является частью ни ISO C, ни POSIX. Файл, как определено во второй версии Single UNIX Specification , просто содержит всю функциональность C89 stdarg.h
, за исключением того, что:
Интерфейс также отличается. printargs
Например, вместо этого можно написать:
#include <stdio.h> #include <varargs.h> /* Типа "void" нет; используйте неявный возврат int. */ printargs ( arg1 , va_alist ) va_dcl /* здесь нет точки с запятой! */ { va_list ap ; int i ; va_start ( ap ); /* указан только va_list! */ for ( i = arg1 ; i >= 0 ; i = va_arg ( ap , int )) printf ( "%d " , i ); va_end ( ap ); putchar ( '\n' ); return ; }
и называется так же.
varargs.h
требует определений функций старого стиля из-за способа реализации. [3] И наоборот, невозможно смешивать определения функций старого стиля с stdarg.h
.