26

我正在使用 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 与 C++)还是输出方法(cout 与 printf)有关,还是只是 MSVC 的一个怪癖?

4

9 回答 9

64

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

这是我的编译器(GCC 4.8.1)告诉我的(甚至没有强制-Wall):

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

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

于 2013-09-30T20:06:04.500 回答
37

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

于 2013-09-30T20:05:44.823 回答
12

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

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

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

C++中 std::coutoperator<<有一个重载 for int,因此在这种情况下将被调用。我们可以看到C++ 草案标准需要许多其他重载,在27.7.3.1 类模板 basic_ostream部分中,我们找到以下运算符声明:

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

为了完整起见,回到未定义的行为,C99 草案标准部分7.19.6.1 The fprintf function whichprintf的部分引用了格式字符串第9段说:

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

于 2013-09-30T20:07:42.573 回答
11

表达方式

18 / 4 + 18 % 4

评估为int

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

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

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

#include <stdio.h>
int main()
{
    printf("%d", 18 / 4 + 18 % 4);
    return 0;
}
于 2013-09-30T20:08:33.157 回答
8

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

“MSVCRT R6002”的快速谷歌说:

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

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

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

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

于 2013-10-01T11:03:24.347 回答
4

在 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);

于 2013-09-30T20:05:38.673 回答
2

如果您想要相同的行为(和响应),您最好编写代码

printf("%d\n", 18/4 + 18%4);

得到一个 int 响应而不是一个浮点响应。<<您将获得与operator selected相同的结果

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

否则,您可以显式使用

printf("%.5f\n", (double)(18/4 + 18%4));

得到6.00000结果。

于 2013-09-30T21:50:26.443 回答
1

让您的数字之一是浮点值:

#include <stdio.h>
int main()
{

    printf("%.0f", 18/4.0+18%4);
    return 0;
} 
于 2013-09-30T20:16:40.307 回答
0

一种可能的替代方法是对文字(或表达式本身)进行类型转换:

#include <stdio.h>

int main(void)
{
    printf("%.5f", (float)18/4+18%4);
    return 0;
}
于 2013-10-01T10:34:48.060 回答