簡體   English   中英

C中的三元運算符

[英]Ternary operator in C

為什么該程序給出了意外的數字(例如: 2040866504-786655336 )?

#include <stdio.h> 
int main()
{
     int test = 0;
     float fvalue = 3.111f;
     printf("%d", test? fvalue : 0);

     return 0;
}

為什么打印出意外的數字而不是0 它應該進行隱式類型轉換嗎? 該程序用於學習目的,沒什么大不了的。

您的平台最有可能在浮點寄存器中傳遞浮點值,並在其他寄存器(或堆棧)中傳遞整數值。 您告訴printf尋找一個整數,因此它正在尋找寄存器中傳入的整數(或在堆棧上)。 但是您將其傳遞給float ,因此零被放置在printf從未看過的浮點寄存器中。

三元運算符遵循語言規則來確定其結果的類型。 有時不能是整數,有時不能是浮點數。 這些可能是不同的大小,存儲在不同的地方,依此類推,這使得不可能生成合理的代碼來處理兩種可能的結果類型。

這是一個猜測。 也許正在發生完全不同的事情。 未定義的行為是未定義的,這是有原因的。 如果沒有大量平台和編譯器細節的經驗和知識,這類事情可能是無法預測的,並且很難理解。 永遠不要讓某人說服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值從二進制轉換為十進制數字的字符串,而%ffloat值執行相同的操作。 通過傳遞fvalue編譯器后,知道它是float型的,但是另一方面,它會看到printf需要一個int類型的參數。 在這種情況下, 有時會達到您的期望,有時會達到我的期望。 有時,它可以實現沒人期望的事情David Schwartz的精彩評論)。
參見測試案例12 %f可以正常工作。

它應該進行隱式類型轉換嗎?

沒有。

盡管現有的贊成答案是正確的,但我認為它們過於技術性,並且忽略了初學者程序員可能具有的邏輯:

讓我們看一下引起某些頭腦混亂的語句:

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表示float0表示整數。 但是,他了解到三元運算符的不同可能結果必須是同一類型! intfloat不是。 在這種情況下,“ float獲勝”, 0變為0.0f

現在printf不是安全的類型。 這意味着您可以錯誤地說“給我打印一個整數”,並在不通知編譯器的情況下傳遞浮點數。 確實發生了。 不管test的值是什么,編譯器都會得出結果將是float類型。 因此,您的代碼等效於:

float x = 0.0f;
printf("%d", x);

此時,您會遇到不確定的行為。 float根本就不是integral什么是預期%d

觀察到的行為取決於您使用的編譯器和計算機。 您可能會看到跳舞的大象,盡管大多數航站樓都不支持該afaik。

什么時候有表達式E1 ? E2 : E3 E1 ? E2 : E3 ,涉及四種類型。 表達式E1E2E3都有一個類型(並且E2E3的類型可以不同)。 此外,整個表達式E1 ? E2 : E3 E1 ? E2 : E3具有類型。

如果E2E3具有相同的類型,那么這很容易:整個表達式具有該類型。 我們可以用如下的元符號表示:

(T1 ? T2 : T2) -> T2 
"The type of a ternary expression whose alterantives are both of the same type T2
 is just T2."

如果它們的類型不同,則事情會變得有些有趣,並且情況與E2E3一起參與算術運算非常相似。 例如,如果將intfloat一起加,則int操作數將轉換為float 那就是程序中正在發生的事情。 類型情況是:

(int ? float : int) -> float

測試失敗,因此int0轉換為float0.0

float值與printf%d轉換說明符不兼容,后者需要int

更准確地說, float值再經歷一次。 float作為尾隨參數之一傳遞給可變參數函數時,它將轉換為double

因此,實際上將double值0.0傳遞給了期望為int printf

在任何情況下,它都是未定義的行為:它是不可移植的代碼,C語言的ISO標准定義不提供其含義。

從這里開始,我們可以應用特定於平台的推理,為什么我們不僅僅看到0 假設int是32位,四字節類型,而double是常見的64位,8字節IEE754表示形式,並且全零位用於0.0 那么,為什么printf不會將此全零位的32位部分視為int0呢?

64位double變量值很有可能在將其放到堆棧上時強制8個字節對齊,可能會將堆棧指針移動4個字節。 然后printf從這四個字節中取出垃圾,而不是從double值中取出零位。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM