为什么来自 printf 的相同表达式的输出与 cout 不同

Why does this output of the same expression from printf differ from cout?

本文关键字:输出 cout 不同 表达式 printf 为什么      更新时间:2023-10-16

我正在使用Visual C++ 2012并从命令行编译以下文件:

#include <stdio.h>
int main()
{
    printf("%.5f", 18/4+18%4);
    return 0;
} 

与 MSVCRT 链接。LIB 而不是 LIBCMT 以避免运行时错误 R6002。
对于此程序,输出的值为 0.00000。

但是,如果我在C++中执行完全相同的事情

 #include <iostream>
 using namespace std;
 int main()
 {
      cout << 18/4+18%4 << endl;
      return 0;
 }

现在,它打印出 6,就像它应该的那样。

有什么区别?是与语言本身(C vs C++(还是输出方法(cout vs printf(有关,还是只是MSVC的一个怪癖?

表达式18/4+18%4的计算结果为 int ,并且您正在请求浮点数。您应该始终在启用警告的情况下进行编译,并注意它们(他们说警告是一个等待发生的错误,他们是对的(。

这是我的编译器(GCC 4.8.1(告诉我的(即使没有强制执行-Wall(:

warning: format ‘%.5f’ expects type ‘double’, but argument 2 has type ‘int’

另一方面,std::cout<<操作能够推断出表达式的类型并将其正确流式传输到屏幕。

C 函数正在传递一个整数,但您告诉它(使用 %f (期望双精度浮点数,因此它失败。C++函数知道它正在传递一个整数,因此它可以正常工作。

C 示例中,此表达式18/4+18%4计算结果为 int,因为所有操作数都是整数常量,但您指定它是 printf双精度,因此处理不正确。另一方面,如果您在表达式的除法部分使用了浮点常量18.0/4+18%4则整个表达式的计算结果将变为双精度数。或者,您也可以在格式说明符中使用"%d"

这也是错误地指定要printf格式的未定义行为,这也说明了为什么使用警告构建很重要,使用 gcc -Wall 我收到以下警告(实时查看(:

warning: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘int’ 

在 C++ std::cout 的运算符中<<具有 int 的重载,因此在这种情况下将调用该运算符。我们可以看到C++草案标准需要的重载,在类模板27.7.3.1部分中basic_ostream我们找到了以下运算符声明:

basic_ostream<charT,traits>& operator<<(int n);

为了完整起见,回到未定义的行为,C99 草案标准在第 7.19.6.1 节中 printf 部分引用的格式字符串第 9 段的 fprintf 函数说:

如果转换规范无效,则行为未定义。[...]

表达式

18 / 4 + 18 % 4

计算结果为 int

但是 printf 格式字符串 "%.5f"> 需要双精度。

使用 c++ 和 ostreams,语言可以自动确定输出类型。

只需将您的 C 代码更改为以下内容:

#include <stdio.h>
int main()
{
    printf("%d", 18 / 4 + 18 % 4);
    return 0;
}

其他人正确地指出了 printf(( 语句中的 int/double 不匹配。 我只想评论你的声明,"与MSVCRT链接。LIB 而不是 LIBCMT 以避免运行时错误 R6002。 这么简单的程序不应该过度地增加运行时环境的负担,因此像这样的运行时错误应该是代码中未定义行为的危险信号。

快速谷歌"MSVCRT R6002"说:

C 运行时错误 R6002 未加载浮点支持

未链接必要的浮点库。 通过检查以下可能的原因进行修复

    该程序是使用需要协处理器的选项(
  1. 如/FPi87(编译或链接的,但该程序在未安装协处理器的计算机上运行。
  2. printf_s或scanf_s函数的格式字符串包含浮点格式规范,程序不包含任何浮点值或变量。
  3. 编译器通过仅在必要时加载浮点支持来最小化程序的大小。编译器无法检测格式字符串中的浮点格式规范,因此它不会加载必要的浮点例程。
  4. 使用浮点参数对应于浮点
  5. 格式规范,或在程序的其他位置执行浮点赋值。这会导致加载浮点支持。
  6. 混合语言程序中,在链接程序时,在 FORTRAN 库之前指定了 C 库。最后重新链接并指定 C 库。

当然,这里的教训是,你应该密切关注编译器和运行时的警告和错误。 如有疑问,请始终假设问题出在您的代码中,而不是编译器中。

在 C 中,因为您在 printf 格式说明符中显式指定浮点 ("%f"(,所以它需要一个浮点参数。 但是你给了它一个"int"参数,因此问题来了。

根据您要执行的操作,您可以:

1( 将你的(否则是整数的(表达式转换为浮点

和/或

2(在cout流中使用setprecision,就像在C中使用"%.5f"一样:

#include <iostream>
using namespace std;
int main()
{
   float x = 18/4+18%4;
   std::cout << std::setprecision(5) << x << endl;
   return 0;
}

3(如果你想要整数,使用printf ("%d", 18/4+18%4);

如果你想要相同的行为(和响应(,你最好编码

printf("%dn", 18/4 + 18%4);

获取 int 响应而不是浮点响应。 您将获得与所选运算符相同的结果<<

ostream& std::operator<<(ostream&, const int);

否则,您可以显式使用

printf("%.5fn", (double)(18/4 + 18%4));

得到6.00000结果。

让其中一个数字成为浮点值:

#include <stdio.h>
int main()
{
    printf("%.0f", 18/4.0+18%4);
    return 0;
} 
<</div> div class="answers">

一种可能的替代方法是,类型转换文本(或表达式本身(:

#include <stdio.h>
int main(void)
{
    printf("%.5f", (float)18/4+18%4);
    return 0;
}