Ответ 1
Исходный код не работает, потому что он пытается использовать printf()
, где ему нужно использовать vprintf()
. Принимая сомнительные точки, такие как выражения logOpen
и logClose
по номинальной стоимости (с учетом обозначений, предположительно это макросы, которые открывают и закрывают поток файлов flog
), код должен быть:
void logPrintf(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
logOpen;
vfprintf(flog, fmt, ap);
logClose;
va_end(ap);
va_list ap2;
va_start(ap2, fmt);
vprintf(fmt, ap2);
va_end(ap2);
}
Нет особых требований использовать две отдельные переменные va_list
; вполне нормально использовать тот же самый в два раза, пока вы используете va_end()
, прежде чем снова использовать va_start()
.
void logPrintf(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
logOpen;
vfprintf(flog, fmt, ap);
logClose;
va_end(ap);
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
Когда значение va_list
передается другой функции (vfprintf()
и vprintf()
в этом коде), вы должны предположить, что она больше не может использоваться в текущей функции. Безопасно называть va_end()
на нем.
В этом коде нет необходимости va_copy()
. Он работает, но он не нужен. Вам нужно va_copy()
в других обстоятельствах, например, когда ваша функция передается va_list
, и вам нужно обработать список дважды:
void logVprintf(const char *fmt, va_list args1)
{
va_list args2;
va_copy(args2, args1);
logOpen;
vfprintf(flog, fmt, args1);
logClose;
vprintf(fmt, args2);
va_end(args2);
}
Обратите внимание, что в этом коде на вызов va_end()
на args1
отвечает код вызывающего кода. Действительно, в стандарте говорится:
Каждое обращение макросов
va_start
иva_copy
должны совпадать с соответствующим вызовом макросаva_end
в той же функции.
Так как функция logVprintf()
не вызывает либо va_start
, либо va_copy
для инициализации args1
, она не может законно называть va_end
на args1
. С другой стороны, стандарт требует, чтобы он вызывал va_end
для args2
.
Теперь функция logPrintf()
может быть реализована в терминах logVprintf()
:
void logPrintf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
logVprintf(fmt, args);
va_end(args);
}
Эта структура - операционная функция, которая принимает va_list
и функцию покрытия, которая принимает многоточие (переменные аргументы) и передает их в операционную функцию после преобразования в va_list
- часто является хорошим способом работы. Рано или поздно вы обычно находите необходимость в версии с аргументом va_list
.