【问题标题】:printf and casting float argumentsprintf 和强制转换浮点参数
【发布时间】:2011-11-13 21:21:51
【问题描述】:

作为我程序的一部分,我使用:

int ret = vprintf (format, args);

args 我进入堆栈,但我不知道实际压入堆栈的内容。 格式是一个字符串,我可以阅读。

在我必须打印浮点数之前,上述方法一直有效。当我打印浮点数时,我得到了一些奇怪的数字......

我检查了如果我调用float fArg = *(reinterpret_cast<const float*>(args) - 然后打印fArg 正确的值被打印(当args 仅由一个实际参数组成时我尝试过)

所以我可能需要 "%...f" 子格式的特殊行为 - 应该强制转换相应的(子)参数 浮动。 (... 表示法表示可以在f 之前添加精度、宽度等) 如何实现?

【问题讨论】:

  • 如果你不知道类型是什么,你怎么可能转换离开它?
  • 我知道应该为 %f 打印的参数需要明确地转换为浮动。如果没有浮动打印 vprintf (format, args);工作正常
  • 但转换为 float (double) 是 printf 将在内部执行的操作。不清楚你认为你需要在这里做什么,或者为什么。
  • @Yakov - reinterpret_cast?你在写什么样的C? ;)
  • @Yakov:那你是在用 C++ 编程,而不是 C。

标签: c casting printf


【解决方案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() 之外,您无法可靠地对值执行任何操作。


今年早些时候,我为 POSIX 增强格式字符串编写了一个 printf() 样式的格式字符串分析器(它支持 n$ 表示法来指定哪个参数指定一个特定的值)。我创建的标头包含(以及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);

解析函数分析源并在结构中设置适当的信息。 create 函数采用结构并创建相应的格式字符串。请注意,示例中的转换说明符 (%3$+-*2$.*1$llX) 是有效的(但有点可疑);它转换作为参数编号 3 传递的 unsigned long long 整数,其宽度由参数 2 指定,精度由参数 1 指定。你可能有更长的格式,但只有几个字符而不重复,即使你使用了十个字符或总共数百个参数。

【讨论】:

  • +1 用于将float 参数自动提升为double - 我的猜测是这就是问题所在......并且足以表明这种事情远非微不足道!跨度>
【解决方案2】:

没有简单、便携的方法可以做到这一点;要检查va_list,您必须知道它包含哪些类型的值,唯一知道的方法是解析格式字符串。实际上,您必须重新实现vprintf 的一部分。 (部分,因为您仍然可以将单独的格式说明符 + 转换值对发送到 printf 而不必担心如何分离 float。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    • 2018-01-23
    • 2015-01-16
    • 2018-10-23
    • 2017-02-11
    • 2011-02-25
    • 2016-08-05
    相关资源
    最近更新 更多