2014-03-03 113 views
12

讓考慮以下程序test.c無符號整數位字段移產量符號整數

#include <stdio.h> 

struct test { 
    unsigned int a:5; 
}; 

int main() { 
    unsigned int i; 
    struct test t = {1}; 
    for (i = 0; i < t.a << 1; i++) 
     printf("%u\n", i); 
    return 0; 
} 

當與gcc -Wsign-compare test.c編譯以下警告產生(用gcc 4.8.1測試):

test.c:9:19: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] 
    for (i = 0; i < t.a << 1; i++) 
       ^

clang -Wsign-compare test.c產生以下結果(用3.2進行測試):

test.c:9:19: warning: comparison of integers of different signs: 'unsigned int' and 'int' [-Wsign-compare] 
    for (i = 0; i < t.a << 1; i++) 
       ~^~~~~~~~~ 
1 warning generated. 

因此,右操作數即移位的無符號位域成爲帶符號的整數。此警告顯示包含1到31之間的任何位字段值。對於更高的值,不會產生警告。這很奇怪。

這用unsigned short,unsigned intunsigned long類型的位字段進行了測試。後者不包含32和64之間的位字段值的任何警告。

當沒有移位完成沒有警告,因此位字段未按預期簽名。

爲什麼比特位大小低於32位的字段在移位時被簽名?我認爲這不是一個錯誤,因爲這與gccclang一致。我必須錯過一些關於位域(或位移)如何工作的信息,但是什麼?如何移動無符號值產生一個有符號值?

回答

7

整數優惠被施加到移位覆蓋在draft C99 standard部分中的操作數6.5.7按位的移位運算符段落它說(重點礦山前進):

整數促銷進行[...]

並且位域的整數提升被覆蓋在部分6.3.1.1布爾,字符,和整數段落它說:

以下可以在表達式中使用的任何地方可能 可以使用一個int或unsigned int:

和包含以下項目符號:

- _Bool,i類型的位域 nt,signed int或unsigned int。

,然後說:

如果int可以表示原始類型的所有值,該值被轉換爲int;否則,它被轉換爲一個unsigned int。 這些被稱爲整數 促銷48)所有其他類型不受整數升級的影響。

它在draft C11標準澄清:

如果int可以表示原始類型(如由寬度的限制,對於一個位字段)的所有值,該值是轉換爲int;否則,它被轉換爲一個unsigned int。這些被稱爲整數促銷。 58)所有其他類型不受整數升級的影響。

所以這是預期的行爲。

+0

而一個31位無符號整數可以表示爲一個int,因此被提升,但是一個32位無符號整數無法被提升並保持不變。我不知道升職。謝謝。 – bootleg

+2

@bootleg如果32位'unsigned int'可以或不可以被提升,則取決於'unsigned int'的寬度。 32位和64位「無符號」的結果不同。 – chux