va_arg()

Получить следующий аргумент из списка аргументов переменной длины

Прототип:

#include <stdarg.h>
type va_arg( va_list param,
type );

Аргументы:

param
Объект va_list, инициализированный макросом va_start().
type
Тип следующего аргумента.

Библиотека:

libc

Описание:

Макрос va_arg() позволяет получить следующий аргумент из списка аргументов переменной длины.


Caution: Будьте особо внимательны при использовании 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) - тип аргумента переданного функции.


Note: Последний аргумент перед многоточием (...) должен быть 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 );
} else
if ( *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 системной библиотеки