Ответ 1
Похоже, вам нужно будет передать указатель на va_list. Для получения дополнительной информации см. Раздел
Предположим, что у меня есть функция, которая принимает переменные аргументы (...
) или va_list
, переданные из другой такой функции. Основная логика заключается в самой этой функции (пусть ее называют f1
), но я хочу, чтобы она передавала va_list
в другую функцию (пусть она называет ее f2
), которая будет определять следующий тип аргумента, получить ее, используя va_arg
и правильно конвертировать и сохранять его для использования вызывающим абонентом.
Достаточно ли передать va_list в f2
, или необходимо передать указатель на va_list. Если va_list не требуется, чтобы быть типом массива или хранить данные о местоположении в месте, на которое указывает объект va_list
(а не сам объект), я не вижу, как передача его по значению может разрешить вызывающую функцию (f1
), чтобы "видеть" изменения вызываемой функции, сделанные с помощью va_arg
.
Может ли кто-нибудь пролить свет на это? Меня интересует то, что требует стандарт, а не то, что позволяет определенная реализация.
Похоже, вам нужно будет передать указатель на va_list. Для получения дополнительной информации см. Раздел
В моем понимании вы должны передать va_list напрямую (а не указатель на него). Кажется, это поддерживается comp.lang.c:
"Va_list сам по себе не является списком переменных аргументов, он действительно является указателем на один. То есть, функция, которая принимает va_list, сама по себе не является varargs, и наоборот".
Я нахожу тексты весьма двусмысленными по этому вопросу. Простейшим может быть, пожалуй, посмотреть в стандарте, как предполагается, что его предопределенные функции с va_list
должны принимать его, например vsnprintf
. И это явно по стоимости, а не по ссылке.
Функции в стандартной библиотеке C пропускают va_list
сам элемент (man 3 vprintf
):
#include <stdarg.h>
int vprintf(const char *format, va_list ap);
int vfprintf(FILE *stream, const char *format, va_list ap);
int vsprintf(char *str, const char *format, va_list ap);
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
Передача указателя на va_list отлично работает в 32-битной системе. Вы даже можете выбрать один параметр в подпрограмме. Но, похоже, он не работает в 64-битной системе, будет генерировать ошибку сегмента в va_arg().
Вы должны передать указатель на va_list
, если вы хотите использовать его в подфункции, а затем не нужно сразу передавать его в va_end
. От C99:
Разрешается создать указатель на
va_list
и передать этот указатель другой функции, и в этом случае исходная функция может дополнительно использовать исходный список после возврата другой функции.
Стандарт позволяет это, однако, на некоторых 64-битных платформах, где va_list
является типом массива, это не работает. Поскольку адрес массива совпадает с адресом первого элемента массива, передача указателя на va_list
приведет к ошибке при вызове va_arg
с указателем на va_list
в качестве аргумента.
Чтобы обойти это, можно получить va_list
в качестве нетрадиционного имени аргумента (обычно с одним или несколькими подчеркиваниями), а затем создать новый локальный va_list
, например, так:
#include <stdarg.h>
int vfoo(va_list ap_)
{
int ret;
va_list ap;
va_copy(ap, ap_);
ret = vbar(&ap);
/* do other stuff with ap */
va_end(ap);
return ret;
}
Это подход, который я использую в своей реализации vsnprintf
для вызова других функций из него для форматирования.