Получить следующий аргумент из списка аргументов переменной длины
#include <stdarg.h>type va_arg( va_list param,type );
va_list
, инициализированный макросом va_start().libc
Макрос va_arg() позволяет получить следующий аргумент из списка аргументов переменной длины.
Будьте особо внимательны при использовании va_arg() на различных платформах. |
va_arg() должен быть использован вместе с соответствующими макросами va_copy(), va_start() и va_end(). Пример:
void example( char *dst, ... ){va_list curr_arg;int next_arg;va_start( curr_arg, dst );next_arg = va_arg( curr_arg, int );...}
В данном примере, переменной next_arg присваивается значение следующего аргумента. Аргумент type (в примере int
) - тип аргумента переданного функции.
Последний аргумент перед многоточием (...) должен быть int или типом, размер которого не изменяется при приведении к int . Иначе стандарт ANSI/ISO говорит, что поведение неопределенно и зависит от компилятора и библиотеки. |
Макрос va_start() должен быть выполнен вначале, чтобы инициализировать curr_arg должным образом, а макрос va_end() после получения всех аргументов.
Переменная curr_arg имеет тип va_list
, который содержит информацию для последовательного получения аргументов.
Следующие функции используют списки “varargs”:
Varargs и приведение типов
На некоторых платформах, таких как PowerPC, тип va_list
type является массивом; на других платформах, таких как x86, нет. Это может привести к проблемам.
Рассмотрим следующий пример. Он кажется корректным, но на платформе PowerPC цифра 2 не будет выведена:
#include <stdio.h>#include <stdarg.h>void handle_foo( char *fmt, va_list *pva ){printf( "%d\n", va_arg( *pva, int ) );}void vfoo( char *fmt, va_list va ){handle_foo( fmt, &va );}void foo( char *fmt, ... ){va_list va;va_start( va, fmt );vfoo( fmt, va );va_end( va );}int main(){foo( "", 2 );return (0);}
Стандарт языка Си говорит, что в протипах подобных vfoo(), массивы приводятся к указателю на базовый тип, поэтому при передаче в функцию объекта типа массив всё будет исправно работать. При использовании в контексте rvalue выражений возвращающих массив, возвращаемое значение преобразуется в указатель на первый элемент, что также будет правильно работать.
При передаче же адреса va_list
другой функции возникает проблема: функция ожидает указатель на массив, однако получает указатель на указатель (из-за изначального преобразования) и при обращении к типу va_list
во второй функции будут получены неверные данные.
Следующий пример исправлен для работы во всех случаях:
#include <stdio.h>#include <stdarg.h>void handle_foo( char *fmt, va_list *pva ){printf( "%d\n", va_arg( *pva, int ) );}void vfoo( char *fmt, va_list va ){va_list temp;va_copy( temp, va );handle_foo( fmt, &temp );va_end( temp );}void foo( char *fmt, ... ){va_list va;va_start( va, fmt );vfoo( fmt, va );va_end( va );}int main(){foo( "", 2 );return (0);}
Использование va_copy() отменяет приведение типов, которое происходит в списке параметров, что позволяет handle_foo() получить верные данные.
Значение следующего аргумента, в соответствии с типом, переданном во втором параметре.
#include <stdio.h>#include <stdarg.h>#include <stdlib.h>static void test_fn( const char *msg,const char *types,... );int main( void ){printf( "VA...TEST\n" );test_fn( "PARAMETERS: 1, \"abc\", 546", "isi", 1, "abc", 546 );test_fn( "PARAMETERS: \"def\", 789", "si", "def", 789 );return (EXIT_SUCCESS);}static void test_fn( const char *msg, /* сообщение для печати */const char *types, /* типы параметров (i,s) */... ) /* аргументы переменной длины */{va_list argument;int arg_int;char *arg_string;const char *types_ptr;types_ptr = types;printf( "\n%s -- %s\n", msg, types );va_start( argument, types );while ( *types_ptr != '\0' ){if ( *types_ptr == 'i' ){arg_int = va_arg( argument, int );printf( "integer: %d\n", arg_int );} elseif ( *types_ptr == 's' ){arg_string = va_arg( argument, char * );printf( "string: %s\n", arg_string );}++types_ptr;}va_end( argument );}
Код генерирует следующий вывод:
$ ./a.out VA...TEST PARAMETERS: 1, "abc", 546 -- isi integer: 1 string: abc integer: 546 PARAMETERS: "def", 789 -- si string: def integer: 789
POSIX 1003.1
va_arg() является макросом.
va_copy(), va_end(), va_start()
Предыдущий раздел: Описание API системной библиотеки