Обратите внимание, что в списках аргументов переменной длины все значения float
повышаются до (и передаются как) double
значения. Вы не можете надежно использовать:
float f = va_arg(args, float); /* BAD! */
потому что язык никогда не помещает значение с плавающей точкой в стек. Вы должны написать:
float f = va_arg(args, double); /* OK */
Это может быть вся ваша проблема.
Если нет, вероятно, вам потребуется отсканировать строку формата, изолировать спецификаторы формата и реализовать значительную часть основного кода printf()
. Для каждого спецификатора вы можете получить соответствующее значение из args
. Затем вы просто вызываете соответствующую функцию printf()
для копии исходного сегмента строки формата (потому что вы не можете изменить оригинал) с правильным значением. Для вашего особого случая вы делаете все, что вам нужно, по-другому.
Было бы неплохо иметь возможность передать параметр args
в vprintf()
, чтобы он имел дело со сбором типа и т. Д., Но я не думаю, что это переносимо (что, несомненно, является неприятностью). После того, как вы передали значение va_list
, например args
, в функцию, которая использует va_arg()
, вы не можете надежно делать что-либо, кроме va_end()
, со значением после возврата из функции.
Ранее в этом году я написал анализатор формата в стиле printf()
для строк формата с расширенным POSIX (который поддерживает нотацию n$
для указания, какой аргумент указывает конкретное значение). Заголовок, который я создал, содержит (наряду с перечислениями для PFP_Errno
, PFP_Status
, FWP_None
и FWP_Star
):
typedef struct PrintFormat
{
const char *start; /* Pointer to % symbol */
const char *end; /* Pointer to conversion specifier */
PFP_Errno error; /* Conversion error number */
short width; /* Field width (FPW_None for none, FPW_Star for *) */
short precision; /* Field precision (FPW_None for none, FPW_Star for *) */
short conv_num; /* n of %n$ (0 for none) */
short width_num; /* n of *n$ for width (0 for none) */
short prec_num; /* n of *n$ for precision (0 for none) */
char flags[6]; /* [+-0# ] */
char modifier[3]; /* hh|h|l|ll|j|z|t|L */
char convspec; /* [diouxXfFeEgGAascp] */
} PrintFormat;
/*
** print_format_parse() - isolate and parse next printf() conversion specification
**
** PrintFormat pf;
** PFP_Status rc;
** const char *format = "...%3$+-*2$.*1$llX...";
** const char *start = format;
** while ((rc = print_format_parse(start, &pf)) == PFP_Found)
** {
** ...use filled in pf to identify format...
** start = pf.end + 1;
** }
** if (rc == PFP_Error)
** ...report error, possibly using print_format_error(pf.error)...
*/
extern PFP_Status print_format_parse(const char *src, PrintFormat *pf);
extern const char *print_format_error(PFP_Errno err);
extern PFP_Status print_format_create(PrintFormat *pf, char *buffer, size_t buflen);
Функция анализа анализирует источник и устанавливает соответствующую информацию в структуре. Функция create берет структуру и создает соответствующую строку формата. Обратите внимание, что спецификатор преобразования в примере (%3$+-*2$.*1$llX
) является допустимым (но немного сомнительным); он преобразует целое число unsigned long long
, переданное в качестве аргумента номер 3, с шириной, заданной аргументом 2, и точностью, заданной аргументом 1. Возможно, вы могли бы иметь более длинный формат, но только на пару символов без повторения, даже если вы использовали десятки или сотни аргументов в общей сложности.