爲什麼這個程序給出了意想不到的數字(例如: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