简体   繁体   English

printf和强制转换float参数

[英]printf and casting float arguments

As a part of my program I use: 作为程序的一部分,我使用:

int ret = vprintf (format, args);

The args I get on the stack and I can't know what actually was pushed on the stack. 我进入堆栈的args ,但我不知道实际将什么压入堆栈。 The format is a string, which I can read. 格式是一个字符串,我可以阅读。

The above approach works until I have to print floats. 在我必须打印浮点数之前,以上方法一直有效。 When I print float I get some strange numbers ... 当我打印浮动时,我得到一些奇怪的数字...

I checked that if I call float fArg = *(reinterpret_cast<const float*>(args) - and then print fArg the correct value is printed (I tried it when args was consisted only from one actual argument) 我检查是否如果我调用float fArg = *(reinterpret_cast<const float*>(args) -然后打印fArg ,则会打印正确的值(当args仅由一个实际参数组成时,我尝试了它)

So probably I need special behavior for "%...f" sub-format - the corresponding (sub)argument should be cast to float. 因此,可能我需要对"%...f"子格式进行特殊处理-应将相应的(sub)参数转换为float。 (The ... notation means that precision, width etc. could be added before f ) How can I implement it? ...表示可以在f之前添加精度,宽度等)。如何实现?

Note that with variable-length argument lists, all float values are promoted to (and passed as) double values. 请注意,对于可变长度参数列表,所有float值都将提升为(并作为double float值传递)。 You cannot reliably use: 您不能可靠地使用:

float f = va_arg(args, float);  /* BAD! */

because the language never places a float value on the stack. 因为语言永远不会在栈上放置浮点值。 You would have to write: 您将必须编写:

float f = va_arg(args, double);  /* OK */

This may be your entire problem. 这可能是您的整个问题。


If not, it is likely that you will need to scan the format string, and isolate the format specifiers, and implement a significant portion of the core printf() code. 如果不是这样,则可能需要扫描格式字符串,隔离格式说明符,并实现核心printf()代码的很大一部分。 For each specifier, you can collect the appropriate value from the args . 对于每个说明符,您可以从args收集适当的值。 You then simply call the appropriate printf() function on a copy of the initial segment of the format string (because you can't modify the original) with the correct value. 然后,您只需使用正确的值在格式字符串的初始段的副本上调用适当的printf()函数(因为您不能修改原始字符串)。 For your special case, you do whatever it is you need to do differently. 对于您的特殊情况,您需要做不同的事情。

It would be nice to be able to pass the args parameter to vprintf() so it deals with collecting the type, etc, but I don't think that's portable (which is undoubtedly a nuisance). 能够将args参数传递给vprintf()很好,这样它就可以处理类型,等等,但是我认为它不是可移植的(无疑是麻烦的)。 After you've passed a va_list value such as args to a function that uses va_arg() on it, you cannot reliably do anything other than va_end() on the value after the function returns. args类的va_list值传递给在其上使用va_arg()的函数后,该函数返回后,您将无法对该值可靠地执行除va_end()之外的任何操作。


Earlier this year, I wrote an printf() -style format string analyzer for POSIX-enhanced format strings (which support the n$ notation to specify which argument specifies a particular value). 今年早些时候,我为POSIX增强的格式字符串(支持n$表示法来指定哪个参数指定特定值)编写了一个printf()风格的格式字符串分析器。 The header I created contains (along with enumerations for PFP_Errno , PFP_Status , FWP_None and FWP_Star ): 我创建的标头包含(以及PFP_ErrnoPFP_StatusFWP_NoneFWP_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);

The parse function analyzes the source and sets the appropriate information in the structure. 解析功能分析源并在结构中设置适当的信息。 The create function takes a structure and creates the corresponding format string. create函数采用结构并创建相应的格式字符串。 Note that the conversion specifier in the example ( %3$+-*2$.*1$llX ) is valid (but a little dubious); 请注意,示例中的转换说明符( %3$+-*2$.*1$llX )是有效的(但有点可疑); it converts an unsigned long long integer passed as argument number 3 with a width specified by argument 2 and a precision specified by argument 1. You probably could have a longer format, but only by a couple of characters without repetition, even if you used tens or hundreds of arguments in total. 它将转换为作为参数编号3传递的unsigned long long整型整数,其宽度由参数2指定,宽度由参数1指定。您可能具有更长的格式,但是只有几个字符没有重复,即使您使用tens也是如此。或总共数百个参数。

There's no easy, portable way to do this; 没有简单,可移植的方式可以做到这一点。 to inspect a va_list , you must know what types of values it holds and the only way to know is by parsing the format string. 要检查va_list ,您必须知道它拥有什么类型的值,唯一的知道方法是解析格式字符串。 Effectively, you'll have to reimplement part of vprintf . 实际上,您必须重新实现vprintf一部分。 (Part, because you can still send off the individual format specifier + cast value pairs to printf and not worry about how to pick apart a float .) (部分,因为您仍然可以将单独的格式说明符+强制转换值对发送给printf ,而不用担心如何分拆float 。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM