printf и приведение аргументов с плавающей точкой - PullRequest
2 голосов
/ 14 ноября 2011

Как часть моей программы я использую:

int ret = vprintf (format, args);

args Я попал в стек и не могу знать, что на самом деле было помещено в стек.Формат - это строка, которую я могу прочитать.

Приведенный выше подход работает до тех пор, пока мне не придется печатать числа с плавающей запятой.Когда я печатаю float, я получаю некоторые странные числа ...

Я проверил, что если я позвоню float fArg = *(reinterpret_cast<const float*>(args) - и затем напечатаю fArg, будет напечатано правильное значение (я пробовал, когда аргументы состояли только изфактический аргумент)

Так что, вероятно, мне нужно специальное поведение для подформата "%...f" - соответствующий (под) аргумент должен быть приведен к float.(Запись ... означает, что точность, ширина и т. Д. Могут быть добавлены до f) Как я могу это реализовать?

Ответы [ 2 ]

4 голосов
/ 14 ноября 2011

Обратите внимание, что в списках аргументов переменной длины все значения 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. Возможно, вы могли бы иметь более длинный формат, но только на пару символов без повторения, даже если вы использовали десятки или сотни аргументов в общей сложности.

1 голос
/ 14 ноября 2011

Нет простого, портативного способа сделать это;чтобы проверить va_list, вы должны знать, какие типы значений он содержит, и единственный способ узнать это - проанализировать строку формата.По сути, вам придется переопределить часть vprintf.(Частично, потому что вы все равно можете отправить отдельные пары спецификатор формата + приведенные значения на printf и не беспокоиться о том, как выделить float.)

...