传递float作为参数搞砸了值

时间:2014-02-14 04:01:45

标签: c floating-point unions

在我的main函数中,我使用以下代码

float f = 32.0;
func("test string %f", f);

func(这些都是示例名称)声明如下

void func(const char *str, ...);

在我的函数实现中,我使用一个名为all_types的联合来获取传递的参数的值

union all_types
{
    void *v;
    CLObject *obj;
    char *s;
    long l;
    char c;
    float f;
    int i;
    double d;
};

然后像这个

给这个联盟一个值
union all_types *o = calloc(1, sizeof(union all_types));
while ((o->v = va_arg(list, void *)) != NULL)

现在,当我知道参数是一个浮点数时,它的值将非常奇怪(我设置了一个断点来计算它)。联合上的il值将是32,正如它们应该的那样。但是,f值是一些奇怪的数字,如0.00000000000000000000000000000000000000000013592595。有谁知道我为什么会这样做?此功能适用于我测试过的每种其他类型的对象。

2 个答案:

答案 0 :(得分:3)

va_arg宏的第二个参数是实际参数的实际类型。 va_arg调用不会发生转换。如果您不知道实际参数的实际类型,那么您运气不好,因为无法找到答案。

请注意,默认参数转换确实发生在调用本身中,因此无法接收floatcharunsigned short。 (float将转换为double,其他两个转换为intunsigned int,具体取决于。)

这就是printf格式使您指定参数类型的原因,float除外。

答案 1 :(得分:3)

您正在执行的操作调用undefined behaviorvariadic functions会将浮动转换为 double ,因为 void *而导致未定义的行为 double 不兼容,因此您无法预期结果。我们可以通过转到draft C99 standard部分7.15.1.1 va_arg宏来看到这一点:

  

[...]如果没有实际的下一个参数,或者type与实际的下一个参数的类型不兼容(根据默认参数提升而提升),则行为未定义,[... ]

执行此操作的正确方法是:

o->d = va_arg(list, double)

你有格式说明符所以这应该是可能的:

"test string %f"
             ^^