2011-09-26 376 views
5

我們發現了一些奇怪的值正在生成,下面是一個小測試用例。 這會打印「FFFFFFFFF9A64C2A」。這意味着無符號的long long似乎已經被延長了。 但是爲什麼? 以下所有類型都是無符號的,所以什麼是符號擴展?預期的輸出 將是「F9A64C2A」。使用無符號long long的符號擴展

#include <stdio.h> 

int main(int argc,char *argv[]) 
{ 
    unsigned char a[] = {42,76,166,249}; 

    unsigned long long ts; 
    ts = a[0] | a[1] << 8U | a[2] << 16U | a[3] << 24U; 

    printf("%llX\n",ts); 


    return 0; 

} 

回答

5

在表達a[3] << 24U,該a[1]具有類型unsigned char。現在, 「整數促銷」 將其轉換爲int,因爲:

以下可以在表達式中用於任何一個或int可以unsigned int 可以使用:

[...]

如果int可以表示原始類型的所有值,則將該值轉換爲 和int; 否則,它將轉換爲unsigned int

(draft) ISO/IEC 9899:1999,6.3.1.1 2)

請還注意,移位運算符(比大多數其它運營商其他)做做「通常的算術轉換」這兩個操作數轉換成普通類型。但是

結果的類型是提升的左操作數的類型。

(6.5.7 3)

在32位的平臺,249 << 24 = 4177526784解釋爲int具有其符號位組。

只是更改爲

ts = a[0] | a[1] << 8 | a[2] << 16 | (unsigned)a[3] << 24; 

修復該問題(後綴爲U常量沒有任何影響)。

+0

小修正:'a [1]'具有'unsigned char'類型。 –

+0

@ user964970:再次閱讀。 'x << y'的類型與'y'的類型無關。 –

+0

@Dietrich Epp:謝謝。 –

1
 
ts = ((unsigned long long)a[0]) | 
    ((unsigned long long)a[1] << 8U) | 
    ((unsigned long long)a[2] << 16U) | 
    ((unsigned long long)a[3] << 24U); 

鑄造防止轉換中間結果來默認的int類型。

+1

但*爲什麼*有一箇中間整型結果,當涉及的所有類型都是無符號類型?罪魁禍首似乎只是第一個'a [0]',用'(無符號)a [0]代替'一切都很好。但爲什麼。 – user964970

1

當從unsigned char自動轉換爲int時,某些移位的a [i]產生符號擴展值。

這符合第6.3.1節算術操作數,第6.3.1.1節C標準草案N1570的布爾,字符和整數,其部分讀取「2.以下內容可用於表達式無論使用int還是unsigned int:... - 具有整型(不包括int或unsigned int)的對象或表達式 其整型轉換等級小於或等於int和unsigned int的等級。 ..如果一個int可以表示原始類型的所有值...,則該值被轉換爲一個int;否則,它被轉換爲一個無符號整數,這些被稱爲整型促銷... 3.整型促銷保留包括符號在內的價值「

例如見www.open-std.org/JTC1/SC22/WG14/www/docs/n1570.pdf

你可以使用如下代碼,其中工程確定:

 int i; 
     for (i=3, ts=0; i>=0; --i) ts = (ts<<8) | a[i]; 
+0

由於常量上的U前綴,在示例代碼中所有被移位的[i]的右邊都是無符號的。 (例如「8U」),意思是例如根據這些規則,表達式a [1] << 8U應該具有無符號類型。 – user964970

+0

@ user964970:罪魁禍首不是'a [0]'。但是,將'a [0]'強制轉換爲'unsigned'會強制執行按位或無符號的結果,從而截斷出現在「a [3] << 24」中的符號擴展名,這是真正的罪魁禍首。 –