变量函数(va_arg)不适用于float,而printf有效吗? 有什么区别?

我碰巧在两年的问题中遇到了类似的情况:

变量函数(va_arg)不适用于float?

有人说,当我们称之为的时候,问题是促使浮动加倍

va_arg(arg, float) 

我的问题是在这篇文章的最后,但首先让我们来看看@ Jack在上面链接的问题下面的答案:

 #include  #include  void foo(int n, ...) { va_list vl; va_start(vl, n); int c; double val; for(c = 0; c < n; c++) { val = va_arg(vl, double); printf("%f\n", val); } va_end(vl); } int main(void) { foo(2, 3.3f, 4.4f); return 0; } 

输出:

 3.300000 4.400000 

现在,如果我们将val = va_arg(vl, double)更改为val = va_arg(vl, float) ,我们将得到(至少我进入MSVS 2012):

 36893488147419103000.000000 2.162500 

我们现在回答我的问题。

在这个主题中: C / C ++ va_list没有正确地返回参数最多的投票答案,而且它的评论说printf float提升double

但有什么区别? 如果它们都将float提升为double ,为什么printf正确地写入值,而va_arg会给我们这样一个鼻子恶魔?

它不是printf ,它将float参数提升为double ,它是编译器 。 换句话说,当你的va_argprintf或任何其他具有可变参数数量的函数获得控件时,所有的float都已被提升为double s; 原始float不可用于检索。

printfva_arg之间的区别在于printf遵循标准设置的规则,并在格式字符串中看到相应的格式说明符时请求提升类型的参数(即double )。 因此,它成功地获得了一个doublefloat值,并产生了所需的输出。

另一方面,当va_arg调用val = va_arg(vl, float)它会忽略促销规则,并获得无效的表示forms。

printf不接受float类型的参数。

例如, "%f"格式说明符需要double类型的参数。 "%Lf"需要long double类型的参数。 没有格式需要float类型的参数(无论如何都会提升为double ,而不是printf本身,而只是因为调用可变函数的语义)。

因此假设printf是用C实现的,并且它使用机制来读取它的参数,在printf的实现中没有为float类型调用va_arg()

任何尝试为类型float调用va_arg()可变函数都将具有未定义的行为,因为此类函数不能有float参数。 printf有效,因为它没有那样做。

变量参数函数的参数获得特殊的促销规则。

这里相关的一个是将float作为变量参数传递给double。 这意味着您无法将参数提取为float,因为它已作为double传递。 这是由编译器完成的,它与printf无关。

这意味着代码val = va_arg(vl, float)无效,因为参数不是float,它是double。 如果你真的需要将传入的值作为浮点数来处理,那么你最多可以做到

 float val = (float) va_arg(vl, double) 

请注意,printf的%f说明符期望参数类型为double ,而不是float

但有什么区别? 如果它们都将float提升为double ,为什么printf正确地写入值,而va_arg会给我们这样一个鼻子恶魔?

除了事实(在问题本身中说明)之外,没有区别, printf的编码方式是将float视为double 。 换句话说,在printf某个地方,当格式字符串包含应该有浮点数的信息时,该函数执行va_arg(vl, double) ,就像你一样。