为什么这个程序给出了意想不到的数字(例如:2040866504
,-786655336
)?三元运算符C
#include <stdio.h>
int main()
{
int test = 0;
float fvalue = 3.111f;
printf("%d", test? fvalue : 0);
return 0;
}
为什么打印意外的数字而不是0
?它应该做隐含的类型转换吗?这个程序对于学习目的来说并不严重。
为什么这个程序给出了意想不到的数字(例如:2040866504
,-786655336
)?三元运算符C
#include <stdio.h>
int main()
{
int test = 0;
float fvalue = 3.111f;
printf("%d", test? fvalue : 0);
return 0;
}
为什么打印意外的数字而不是0
?它应该做隐含的类型转换吗?这个程序对于学习目的来说并不严重。
最有可能的,平台通行证在一个不同的寄存器浮点值的浮点寄存器和整数值(或在堆栈上)。你告诉printf
寻找一个整数,所以它看着寄存器整数传入(或在堆栈中)。但是你通过了一个float
,所以零被放置在printf
永远不会看到的浮点寄存器中。
三元运算符遵循语言规则来决定其结果的类型。它有时不能是一个整数,有时候是一个浮点数。这些可能是不同的大小,存储在不同的地方等等,这将使得不可能生成合理的代码来处理可能的结果类型。
这是一个猜测。也许完全不同的事情正在发生。出于某种原因未定义的行为未定义。如果没有大量关于平台和编译器细节的经验和知识,这些类型的东西可能无法预测,也很难理解。永远不要让别人说服你,因为UB似乎在他们的系统上工作,所以UB没问题或者安全。
因为您正在使用%d
来打印float
值。使用%f
。使用%d
打印float
值会调用未定义的行为。编辑: 关于OP的评论;
为什么打印随机数而不是0?
当你编译这段代码,编译器应该给你一个警告:
[Warning] format '%d' expects argument of type 'int', but argument 2 has type 'double' [-Wformat]
此警告的是,这行代码调用一个未定义的行为的自我解释。这是因为,转换规范%d
指定printf
将int
值从二进制转换为十进制数字的字符串,而%f
对float
值的做法相同。通过fvalue
编译器知道它是float
类型,但另一方面它看到printf
需要int
类型的参数。在这种情况下,有时会达到您的预期,有时会达到我的预期。有时它没有人期望(由David Schwartz尼斯评论)。
查看测试案例1和2。它与%f
工作正常。
它应该做隐式类型转换吗?
号
虽然现有upvoted答案是正确的,我觉得他们都太技术而忽略逻辑初学者程序员可能有:
让我们来看看本声明的一些首脑造成混乱:
printf("%d", test? fvalue : 0);
^^ ^ ^
| | | |
| | | - the value we expect, an integral constant, hooray!
| | - a float value, this won't be printed as the test doesn't evaluate to true
| - an integral value of 0, will evaluate to false
- We will print an integer!
编译器看到的有点不同。他同意test
的含义false
的值。他同意fvalue
是一个整数float
和0
。但是,他了解到三元运算符的不同可能结果必须是相同的类型! int
和float
都没有。在这种情况下,“float
获胜”,0
变成0.0f
!
现在printf
是不安全的。这意味着你可以错误地说“打印一个整数”并传递一个浮点数,而不用编译器注意。确实发生了。无论test
的值是多少,编译器都会推断结果将是float
。因此,您的代码相当于:
float x = 0.0f;
printf("%d", x);
此时,您遇到未定义的行为。 float
根本不是什么integral
%d
预计什么。
观察到的行为取决于您正在使用的编译器和机器。你可能会看到跳舞的大象,尽管大多数终端不支持afaik。
当我们有表达式E1 ? E2 : E3
时,涉及四种类型。表达式E1
,E2
和E3
每个都有一个类型(并且E2
和E3
的类型可以不同)。此外,整个表达式E1 ? E2 : E3
有一个类型。
如果E2
和E3
具有相同的类型,则很容易:整体表达式具有该类型。
(T1 ? T2 : T2) -> T2
"The type of a ternary expression whose alterantives are both of the same type T2
is just T2."
如果它们不具有相同的类型,事情变得有些有趣,而且情况颇为相似E2
和E3
被卷入一起算术:我们可以通过元标记这样表达这种操作。例如,如果您将int
和float
加在一起,那么int
操作数将转换为float
。这就是你的程序中发生的事情。类型的情况是:
(int ? float : int) -> float
测试失败,并且因此int
值0
转换为float
值0.0
。
此float
值与%d
转换说明符printf
不兼容,这需要int
。
更确切地说,float
值经历了一次。当float
作为可变参数函数的尾随参数之一传递时,它将转换为double
。
所以实际上double
值0.0正在传递到printf
它期望int
。
在任何情况下,它都是未定义的行为:它是不可移植的代码,C语言的ISO标准定义不提供含义。
从这里开始,我们可以应用特定于平台的推理,为什么我们不会看到0
。假设int
是32位,4字节类型,并且double
是常见的64位,8字节,IEE754表示,并且全位零用于0.0
。 那么,为什么不是printf
作为int
值0
处理的全零位的32位部分?
很可能,64位double
参数值在放入堆栈时强制8字节对齐,可能会将堆栈指针移动四个字节。然后printf
从这四个字节中取出垃圾,而不是从double
值的零位。
你是什么意思的“随机”?你试图将一个浮点数作为一个整数来打印,但是这个数字应该是'0',它们在两种类型中都有相同的位表示。 –
随机,或只是不正确? –
这个问题似乎是脱离主题,因为它是关于谁知道什么。 – 2013-09-28 05:08:33