【问题标题】:Why does this function print 0 when I pass in a nonzero value as its argument?为什么当我传入一个非零值作为参数时,这个函数会打印 0?
【发布时间】:2016-10-06 21:53:21
【问题描述】:

这里的代码:

#include <stdio.h>

int main(void) {
    test(7.4, 4);
    return 0;
}

void test(float height, float radius){
    printf("%f", height);    
}

将打印:

0.000000

这是为什么?为什么它不只打印 7.4?

【问题讨论】:

  • test() 定义放在 main 上会发生什么?当您在 7.4 后缀 f(例如 7.4f)时会发生什么?照原样,您的代码不应该在符合标准的编译器下编译 - 如果是这样,我的猜测是原型是隐式创建的,因为它不在 main 之上并导致一些奇怪的问题。我也不确定这是否是真的您的代码,因为它看起来已被破解并且无法编译。你能发一个SSCCE吗?
  • 你看过编译器警告了吗?
  • Qix 所说的一切。 gcc 版本ideone 使用打印-0.00000
  • @qix 哇,把test() 放在main() 上面完全解决了它。你知道这是为什么吗?
  • @Alfie,templatetypedef给出了详尽的解释

标签: c function function-prototypes


【解决方案1】:

在您编写的程序中,您调用了test 函数,而没有先对其进行原型设计。现代编译器通常会拒绝这一点,但在较旧的编译器 - 或为旧 C 代码提供支持的编译器 - 程序将隐式尝试推断参数类型。您提供了7.44 作为参数,这意味着编译器希望您将分别传入doubleint,因为7.4double 文字,因此它生成代码以将第一个参数作为double 传递,将第二个参数作为int 传递。

稍后,当您实际定义test 时,您指定参数为floats,这与之前的代码不匹配。结果,该函数尝试读取它的第一个参数,就好像它是float,所以它最终以某种方式重新解释了一些字节,碰巧将它们解释为接近负零的浮点数。

要解决此问题,请在调用 test 函数之前对其进行原型化,或者在使用之前对其进行定义。请注意,带有警告的编译器会明确告诉您隐式声明和定义不匹配:

nodecl.c: In function ‘main’:
nodecl.c:4:3: warning: implicit declaration of function ‘test’ [-Wimplicit-function-declaration]
   test(7.4, 4);
   ^
nodecl.c: At top level:
nodecl.c:8:6: warning: conflicting types for ‘test’
 void test(float height, float radius){
      ^
nodecl.c:4:3: note: previous implicit declaration of ‘test’ was here
   test(7.4, 4);
   ^

展望未来,如果您看到这些警告,您现在知道它们在说什么,并且您应该能够更快地诊断出您的错误。

【讨论】:

  • 如果 7.4(以 IEEE754 双精度格式表示)被误解为两个相邻的单精度值,则它们是 -1.5881868392106856e-232.4624998569488525。当以%f 格式打印时,第一轮到-0.000000
  • 查看@MarcoGuerri 的answer 以获得对那些如此倾向的人的字节码解释。
【解决方案2】:

在带有 gcc 的 Linux 下会发生这种情况:编译器将 7.4 作为双精度传递,因为 test 原型仅在 main 之后定义。

0000000000400506 <main>:
  400506:       55                      push   %rbp
  400507:       48 89 e5                mov    %rsp,%rbp
  40050a:       48 83 ec 10             sub    $0x10,%rsp
  40050e:       48 b8 9a 99 99 99 99    movabs $0x401d99999999999a,%rax
  400515:       99 1d 40 
  400518:       bf 04 00 00 00          mov    $0x4,%edi
  40051d:       48 89 45 f8             mov    %rax,-0x8(%rbp)
  400521:       f2 0f 10 45 f8          movsd  -0x8(%rbp),%xmm0
  400526:       b8 01 00 00 00          mov    $0x1,%eax
  40052b:       e8 07 00 00 00          callq  400537 <test>
  400530:       b8 00 00 00 00          mov    $0x0,%eax
  400535:       c9                      leaveq 
  400536:       c3                      retq  

0x401d99999999999a 是你的 64 位双精度,即 7.4。 test 函数将该值解释为单精度数字(浮点数,如原型中指定),使用cvtss2sd(浮点数到双精度数转换)通过xmm0 将其传递给printf

0000000000400537 <test>:
  400537:       55                      push   %rbp
  400538:       48 89 e5                mov    %rsp,%rbp
  40053b:       48 83 ec 10             sub    $0x10,%rsp
  40053f:       f3 0f 11 45 fc          movss  %xmm0,-0x4(%rbp)
  400544:       f3 0f 11 4d f8          movss  %xmm1,-0x8(%rbp)
  400549:       f3 0f 5a 45 fc          cvtss2sd -0x4(%rbp),%xmm0
  40054e:       bf e4 05 40 00          mov    $0x4005e4,%edi
  400553:       b8 01 00 00 00          mov    $0x1,%eax
  400558:       e8 83 fe ff ff          callq  4003e0 <printf@plt>
  40055d:       c9                      leaveq 
  40055e:       c3                      retq   
  40055f:       90                      nop

这只会将初始 64 位值的一小部分转换为打印到标准输出的实际双精度值,即 0x9999999a,产生接近 0 的结果。

【讨论】:

    【解决方案3】:

    打开编译器警告 - 并修复它警告的内容。 为test() 函数使用函数原型。

    【讨论】:

      【解决方案4】:

      我在 GGC 编译器中编译了你的程序,出现以下错误

       float.c:11:6: warning: conflicting types for ‘test’ [enabled by default]
       void test(float height, float radius)
            ^
      float.c:7:5: note: previous implicit declaration of ‘test’ was here
           test(7.4, 4);
           ^
      

      然后我在 main() 函数之前添加了test() 函数的原型

      void test(float, float);
      

      然后再次编译,我得到了正确的输出。所以,添加test()函数的原型。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-12-02
        • 1970-01-01
        • 2016-11-01
        • 2014-04-19
        • 2023-03-09
        • 2013-02-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多