代码之家  ›  专栏  ›  技术社区  ›  Steve Schnepp

为什么在printf中int到float转换失败?

  •  2
  • Steve Schnepp  · 技术社区  · 3 年前

    当使用int到float隐式转换时,它会因printf()而失败

    #include <stdio.h>
    
    int main(int argc, char **argv) {
        float s = 10.0;
        printf("%f %f %f %f\n", s, 0, s, 0); 
        return 0;
    }
    

    使用编译时 gcc -g scale.c -o scale 它确实输出垃圾

    ./scale
    10.000000 10.000000 10.000000 -5486124068793688683255936251187209270074392635932332070112001988456197381759672947165175699536362793613284725337872111744958183862744647903224103718245670299614498700710006264535590197791934024641512541262359795191593953928908168990292758500391456212260452596575509589842140073806143686060649302051520512.000000
    

    如果我显式地将整数强制转换为浮点,或者使用 0.0 (这是一个 double )它按设计工作。

    #include <stdio.h>
    
    int main(int argc, char **argv) {
        float s = 10.0;
        printf("%f %f %f %f\n", s, 0.0, s, 0.0); 
        return 0;
    }
    

    使用编译时 gcc-g刻度。c-o刻度 它确实输出了预期的输出

    ./scale
    10.000000 0.000000 10.000000 0.000000
    

    发生了什么?

    我正在使用 gcc (Debian 10.2.1-6) 10.2.1 20210110 如果这很重要的话。

    2 回复  |  直到 3 年前
        1
  •  6
  •   Vlad from Moscow    3 年前

    转换说明符 f 需要类型为的对象 double 通常 sizeof( double ) 等于 8 虽然 sizeof( int ) 等于 4 此外,整数和二重具有不同的内部表示。

    使用不正确的转换说明符会导致未定义的行为。

    来自C标准(7.21.6.1 fprintf函数)

    9如果转换规范无效,则行为为 未定义.275) 如果任何参数不是 相应的转换规范,行为是未定义的。

    对于类型的对象 float 然后由于 默认参数提升 它们被转换为类型 双重的

    来自C标准(6.5.2.2函数调用)

    6如果表示被调用函数的表达式的类型 不包括原型,整数促销在 每个参数以及类型为float的参数都被提升为 双重的这些被称为 默认参数提升

    因此 printf

    printf("%f %f %f %f\n", s, 0.0, s, 0.0); 
    

    printf("%f %f %f %f\n", s, 0.0f, s, 0.0f); 
    

    相对于结果是等效的。

    请注意,一些程序员在输出doubles时使用长度修饰符 l 例如在转换规范中 %lf 。但是,长度修改器没有任何作用,应该删除。

        2
  •  4
  •   Andreas Wenzel    3 年前

    variadic函数需要一些关于它传递的参数数量和类型的信息,因为它必须主动检索那些使用 va_arg (这需要将参数的类型作为参数检索)。假使 printf ,此信息在第一个参数中,即格式字符串。

    如果有 %f 在格式字符串中,variadic函数将期望 double 论点 1. ,并将使用检索此 va_arg

    但编译器对此一无所知 2. 。它不考虑格式字符串--它看到的只是 真实的 参数的类型,在本例中为 int

    所以有一个 int 放入,然后 双重的 1. 取出。这恰好是未定义的行为。

    你的 观察到的 行为是因为您的第一个平台传递的浮点值不同于整数值——整数 0 被放在一个地方,但是 va_arg 从另一个地方读——事实上,读第二个实际的 float 1. 第一次争论,不管发生了什么 第三 浮动 1. 如果你通过了一个,就会有价值。


    1. 浮动 变元函数的自变量自动扩展为 双重的

    2. 能够 ,而现代编译器可以生成 警告 如果格式字符串和参数类型不匹配。但编译器不是 允许的 根据格式字符串更改生成的代码 输出函数 行为与例如自定义变差函数不同。