#c #casting #printf
#c #Кастинг #printf
Вопрос:
Как часть моей программы я использую:
int ret = vprintf (format, args);
args
Я попадаю в стек, и я не могу знать, что на самом деле было помещено в стек.
Формат представляет собой строку, которую я могу прочитать.
Описанный выше подход работает до тех пор, пока мне не придется печатать значения с плавающей запятой. Когда я печатаю float, я получаю какие-то странные числа…
Я проверил, что если я вызываю float fArg = *(reinterpret_cast<const float*>(args)
— а затем печатаю fArg
, выводится правильное значение (я пробовал это, когда аргументы состояли только из одного фактического аргумента)
Поэтому, вероятно, мне нужно особое поведение для "%...f"
подформата — соответствующий (sub) аргумент должен быть приведен к float. ( ...
Обозначение означает, что точность, ширина и т.д. Могли быть добавлены раньше f
) Как я могу это реализовать?
Комментарии:
1. Если вы не знаете, что это за тип, как вы могли бы его преобразовать?
2. Я знаю, что аргумент, который должен быть напечатан для %f, должен быть явно приведен к плавающей точке. Если нет плавающих значений, напечатанных vprintf (формат, аргументы); работает нормально
3. Но приведение к float (double) — это то, что printf собирается делать внутренне. Неясно, что, по вашему мнению, вам нужно здесь делать или почему.
4. @Yakov —
reinterpret_cast
? Какой тип C вы пишете? 😉5. @Yakov: Тогда вы программируете на C , а не C.
Ответ №1:
Обратите внимание, что в списках аргументов переменной длины все 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, amp;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);
Функция parse анализирует источник и устанавливает соответствующую информацию в структуре. Функция create принимает структуру и создает соответствующую строку формата. Обратите внимание, что спецификатор преобразования в примере ( %3$ -*2$.*1$llX
) является допустимым (но немного сомнительным); он преобразует unsigned long long
целое число, переданное в качестве аргумента номер 3, с шириной, указанной аргументом 2, и точностью, указанной аргументом 1. Вероятно, у вас мог бы быть более длинный формат, но только на пару символов без повторений, даже если вы использовали в общей сложности десятки или сотни аргументов.
Комментарии:
1. 1 за автоматическое перемещение
float
аргументов вdouble
— я предполагаю, что это проблема … и за предоставление достаточного количества, чтобы указать, что такого рода вещи далеки от тривиальных!
Ответ №2:
Простого переносимого способа сделать это не существует; чтобы проверить va_list
, вы должны знать, какие типы значений он содержит, и единственный способ узнать это — проанализировать строку формата. По сути, вам придется переопределить часть vprintf
. (Частично, потому что вы все еще можете отправить отдельные пары спецификатор формата приведенное значение в printf
и не беспокоиться о том, как выделить float
.)