2011-06-02 94 views
4

考慮這個C代碼:爲什麼C不運行unsigned int與負值的比較?

#include "stdio.h" 

int main(void) { 

    int count = 5; 
    unsigned int i; 

    for (i = count; i > -1; i--) { 
     printf("%d\n", i); 
    } 
    return 0; 
} 

我的觀察/問題:循環永遠不會被執行。但是,如果我將數據類型從unsigned int更改爲int,則所有內容都按預期工作。

我一直在考慮未簽名的整數作爲值,「環繞」,當你試圖保持減去他們。所以,當我是零,我減1,它會繞到UINT_MAX。而且由於它的價值從不消極,所以這將是一個無限循環。 (而這正是當我將比較從i> -1改變爲i> = 0時發生的情況。)

我的邏輯中某處出現故障,因爲如果我是無符號的,循環永遠不會執行,而且我將它與-1進行比較。編譯器會以某種方式優化它,或者運行時值的行爲與我期望的不同。

爲什麼循環不能運行?

+0

您是否嘗試過直接轉換字面值-1? – 0xC0000022L 2011-06-02 17:17:11

+0

你是完全正確的。但請記住,5> UINT_MAX是錯誤的。 – sidyll 2011-06-02 17:18:29

+0

http://stackoverflow.com/questions/2084949/arithmetic-operations-on-unsigned-and-signed-integers http://stackoverflow.com/questions/24466857/why-sizeofint-is-not-greater-than- 1 http://stackoverflow.com/questions/3100365/why-is-%E2%88%921-sizeofint – 2015-03-24 11:59:15

回答

18

i > -1中,-1被轉換爲unsigned int,導致值爲UINT_MAXi從不大於該值,所以循環體從不執行。

您可能會發現,您可以說服您的編譯器警告您:在條件上下文中使用always-true或always-false表達式。但是,如果您編寫了i > -2,那麼仍然無法幫助您,因此您可能還會發現可以爲所有混合符號比較啓用警告。

請注意,在C中,算術是總是與相同類型的操作數執行。這包括比較,但IIRC不是輪班操作員。如果操作數是不同類型的,就像在這種情況下那樣,至少其中一個操作數被轉換爲相同類型。處理目標類型的規則在6.3.1.1/2和6.3.1.8/1中。

+0

新手問題:你提到的章節號是什麼規格? – Jaanus 2011-06-02 17:29:53

+2

@Jaanus C99的http://www.open-std.org/JTC1/sc22/wg14/www/docs/n1256.pdf – Wiz 2011-06-02 17:34:00

+1

@Jaanus:C99(準確地說是n1256)。在C89中是3.2.1.1和3.2.1.5。 – 2011-06-02 17:36:08

3

當您混合簽署並在「類型對稱」二進制運算相同寬度的無符號的操作數(在你的例子一樣+*>),無符號的類型「勝」,並且操作中的無符號域中被評估。即帶符號的操作數被轉換爲無符號類型。

在您的示例中,整數常量的類型爲signed int,而i的類型爲unsigned int。操作數具有相同的寬度,因此在您的示例中,i > -1被解釋爲i > (unsigned) -1,相當於i > UINT_MAX。這就是爲什麼你的循環從不執行。

+0

一般來說,這是不正確的,無符號類型獲勝。試試,例如'unsigned char'和'int'。 – vitaut 2011-06-02 17:23:36

+0

@vitaut:是的,你說得對,但實際上這不是一個很好的例子。在表達式中使用'unsigned char'時,通常的算術轉換首先應用,'unsigned char'很早就變成'signed int'。它發生在我在我的回答中提到的「二元」規則有一個機會踢。因此,形象地說,你只是「不能」在表達式中使用'unsigned char' - 它不會堅持。在任何其他事情都有機會發生之前,它將捕捉到「int」。 – AnT 2011-06-02 17:31:17

+0

爲了更好的例子,'unsigned int'失去了'signed long long',假設(long是常見的)long long可以表示所有'unsigned int'的值。所以'(unsigned int)5> -1LL'在大多數實現中都是正確的,但是如果'int'和'long long'具有相同的寬度,則爲false。這是一個非常好的理由,不要在'unsigned int'和'long'之間進行混合比較:你將在LP64機器上得到一個有符號的比較,並且在LLP64和(可能)32位上得到一個無符號的比較。 – 2011-06-02 17:46:30

0

-1在無符號比較中變成UINT_MAX。由於沒有數字大於此值,因此循環條件從不爲真,並且循環從不輸入。

如果將其更改爲i >= 0,則應該按預期工作。其實你可能不應該在這種情況下使用unsigned :-)

+1

如果你把它改成'i> = 0',那麼它總是真的,因此是無用的。 – 2011-06-02 17:47:36

0

無論你是處理無符號還是帶符號的數字,-1總是被編譯爲0xffffffff。處理器具有帶符號和無符號比較標誌。將該數字與5進行比較時,簽名標誌會將其視爲-1並表示它較少,但無符號標誌會將其視爲一個較大的數字並表示它較大。由於該數字也與UINT_MAX相同,因此所有無符號數字的比較結果都將爲false。

+0

「始終編譯爲0xffffffff」(4,294,967,295) - 這不是真的,該標準不能保證'UINT_MAX'的值。實際上,它所說的全部是「它們的實現定義的值應等於或大於65535(2^16-1)的幅度(絕對值)」 – Wiz 2011-06-02 17:44:30