简体   繁体   English

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

[英]Variadic function (va_arg) doesn't work with float, while printf does? What the difference is?

I just happened to have similar situation like in this question from two years: 我碰巧在两年的这个问题中遇到了类似的情况:

Variadic function (va_arg) doesn't work with float? 变量函数(va_arg)不适用于float?

There is said that the problem is promoting float to double when we call things like 有人说,当我们称之为的时候,问题是促使浮动加倍

    va_arg(arg, float)

My question is at the end of this post, but first let's take a look at @Jack's answer below the question linked above: 我的问题是在这篇文章的最后,但首先让我们来看看@ Jack在上面链接的问题下面的答案:

#include <stdio.h>          
#include <stdarg.h>

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;
}

Output: 输出:

3.300000
4.400000

Now, if we change val = va_arg(vl, double) into val = va_arg(vl, float) , we will get (at least I get in MSVS 2012): 现在,如果我们将val = va_arg(vl, double)更改为val = va_arg(vl, float) ,我们将得到(至少我进入MSVS 2012):

36893488147419103000.000000
2.162500

Let's go into my question now. 我们现在回答我的问题。

In this topic: C/C++ va_list not returning arguments properly most voted answer and it's comment says that printf also promotes float 's to double 's. 在这个主题中: C / C ++ va_list没有正确地返回参数最多的投票答案,并且它的评论说printf float提升double

But what is difference? 但有什么区别? If both of them promotes float into double , why printf writes values correctly, while va_arg gives us such a nasal demons? 如果它们都将float提升为double ,为什么printf正确地写入值,而va_arg会给我们这样一个鼻子恶魔?

It's not printf that promotes the float argument to double , it is compiler that does it. 它不是printf ,它将float参数提升为double ,它是编译器 In other words, by the time your va_arg or printf or any other function with variable number of arguments gets the control, all the float s are already promoted to double s; 换句话说,当你的va_argprintf或任何其他具有可变参数数量的函数获得控件时,所有的float都已被提升为double s; the original float s are not available for retrieval. 原始float不可用于检索。

The difference between printf and va_arg is that printf follows the rules set up by the standard, and request the parameter of a promoted type (ie double ) when it sees the corresponding format specifier in the format string. 之间的差printfva_argprintf遵循由标准设立的规则,并请求一个提升的类型(即,参数double )时,看到在格式字符串对应的格式说明。 Hence it successfully gets a double with the promoted value of the float in it, and produces the desired output. 因此,它成功地获得了一个doublefloat值,并产生了所需的输出。

On the other hand, when va_arg calls val = va_arg(vl, float) it ignores the promotion rule, and gets an invalid representation in return. 另一方面,当va_arg调用val = va_arg(vl, float)它会忽略促销规则,并获得无效的表示形式。

Arguments to variable argument functions gets special promotion rules. 变量参数函数的参数获得特殊的促销规则。

The one relevant here is that passing a float as a variable argument gets promoted to a double. 这里相关的一个是将float作为变量参数传递给double。 Meaning you cannot extract the argument as a float, as it has been passed as a double. 这意味着您不能将参数作为float提取,因为它已作为double传递。 This is done by the compiler, it has nothing to do with printf . 这是由编译器完成的,它与printf无关。

This means the code val = va_arg(vl, float) is not valid, as the argument is not a float, it's a double. 这意味着代码val = va_arg(vl, float)无效,因为参数不是float,它是double。 If you really need to deal with the passed in values as a float, at best you can do 如果你真的需要将传入的值作为浮点数来处理,那么你最多可以做到

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

Note that the %f specifier for printf expects an argument of type double , not a float 请注意,printf的%f说明符期望参数类型为double ,而不是float

printf does not take arguments of type float . printf不接受float类型的参数。

The "%f" format specifier, for example, requires an argument of type double . 例如, "%f"格式说明符需要double类型的参数。 "%Lf" requires an argument of type long double . "%Lf"需要long double类型的参数。 There is no format that requires an argument of type float (which would be promoted to double anyway, not by printf itself but simply because of the semantics of calls to variadic functions). 没有格式需要一个float类型的参数(无论如何都会提升为double ,而不是printf本身,而只是因为调用variadic函数的语义)。

So assuming printf is implemented in C, and that it uses the <stdarg.h> mechanisms to read its arguments, there is no invocation of va_arg() for type float in the implementation of printf . 因此假设printf是用C实现的,并且它使用<stdarg.h>机制来读取它的参数,在printf的实现中没有为float类型调用va_arg()

Any variadic function that does attempt to invoke va_arg() for type float will have undefined behavior, since there can be no float arguments to such a function. 任何尝试为类型float调用va_arg()可变函数都将具有未定义的行为,因为这样的函数不能有float参数。 printf works because it doesn't do that. printf有效,因为它没有这样做。

But what is difference? 但有什么区别? If both of them promotes float into double , why printf writes values correctly, while va_arg gives us such a nasal demons? 如果它们都将float提升为double ,为什么printf正确地写入值,而va_arg会给我们这样一个鼻子恶魔?

There's no difference, except the fact (stated in the question itself), that printf is coded in a way to treat float as double . 除了事实(在问题本身中说明)之外,没有区别, printf的编码方式是将float视为double In other words, somewhere inside printf , when the format string contains information that there should be a floating point number, the function does va_arg(vl, double) , just like you did. 换句话说,在printf内部的某个地方,当格式字符串包含应该有浮点数的信息时,该函数会执行va_arg(vl, double) ,就像你一样。

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

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