2012-06-24 168 views
0

可能重複:
Unsigned long with negative value
Assigning negative numbers to an unsigned int?無符號和符號整數

#include<stdio.h> 

int main() 
{ 
    struct a 
    { 
     unsigned int i:3; 
     int c:3; 
    }s; 
    s.i=5; 
    s.c=5; 

    printf("s.i=%d\n",s.i); 
    printf("s.c=%u\n",s.c); 
    unsigned int x = -1; 
    printf(" x = %d", x); 
    return 0; 
} 

此輸出:

s.i=5 
s.c=4294967293 
x=-1 

我不清楚「x」和「sc」的輸出(sc可以只存儲3位數的數據,但是在輸出中它給出了非常大的值)。「x」被聲明爲無符號的,所以存儲在x中的位是1111111 .......並且x的輸出應該是一個較大的值而不是-1。第一個printf()語句按預期給出結果 我正在使用devC++編譯器。

+3

你爲什麼不縮進你的代碼? –

+0

所有的參數都以'int'的形式傳遞給'printf',由您來設置合適的格式說明符。 –

回答

2

好像有兩件事情會在需要理解:

  • printf()轉換說明
  • 積分轉換

而且兩件事個在幫助你的輸出感:

  • 參數晉升爲可變參數函數的參數
  • 補碼錶示

首先,printf()是一個可變參數函數。它不知道它的參數是什麼類型(除了格式字符串),所以你必須使用轉換說明符來告訴它如何解釋參數。這些參數受「默認參數促銷」的影響,因此您的3位位域被提升爲ints。

您正在使用轉換符(%d%u%d)不匹配數據的符號性,所以你會得到取決於如何你的數據在內存中其實是代表未定義的行爲。

二,C11標準規定:

6.3.1。3個符號和無符號整數

  • 當與整數類型的值被轉換爲比其它_Bool另一個整數類型,如果該值可以通過新的類型來表示,它是不變的。否則,如果新類型是無符號的,則通過反覆地增加或減去比新類型中可表示的最大值多一個值,直到該值處於新類型的範圍內來轉換該值。

  • 否則,新類型被簽名並且其值不能被表示;結果是實現定義的或實現定義的信號被引發。

(據我所知道的,細節相關的至少從C89這裏是真實的。)

這告訴我們你的代碼有兩件事情:

  • 當您將-1分配給unsigned int時,將UINT_MAX + 1添加到它,給出3239位整數的UINT_MAX4294967295

  • 當您嘗試將5分配給3位有符號位域時,結果是實現定義的。

所以,你有兩個不確定和實現定義的行爲,但我們仍然可以嘗試讓你的輸出感,只是爲了好玩。我假設32位整數和two's complement表示。

你的系統是存儲在x429496729511111111 11111111 11111111 11111111。當你告訴printf()你通過它的參數被簽名時,那些相同的位被解釋爲-1,這是你得到的輸出。

對於s.c,你似乎已經得到了實現定義很簡單: 的三位代表101得到5原樣保存。這意味着使用正確的轉換說明符,printf()應顯示s.c-3

這裏是您指定的值:

s.i = 101 
s.c = 101 
    x = 11111111 11111111 11111111 11111111 

3位值由左填充用0提升到32位的無符號值和重複的符號值的符號:

s.i = 00000000 00000000 00000000 00000101 
s.c = 11111111 11111111 11111111 11111101 
    x = 11111111 11111111 11111111 11111111 

,當解釋爲符號,無符號和符號整數給出:

s.i=5 
s.c=4294967293 
x=-1 

x=-1暗示我實際上使用了二進制補碼錶示法(這是一個非常安全的賭注,無論如何),並且s.c的輸出表明您的32位寬。

+1

它是一個很好的解釋。我有一個小小的疑問來澄清。如果sc = 3(011)的值,那麼它將被轉換爲sc = 00000000 00000000 00000000 00000011爲32位整數。由於sc是帶符號的,並且其值5(101)表示它爲負數,因此當轉換爲32位整數時,除了最後三個比特位置之外,在所有比特位置中都有1(在這種情況下,最後三個比特本身就是數字)。 – sourabh912

+0

@ sourabh912是的,你有它。請注意,111 ... 101實際上是-3。 (所有這些都假設二進制補碼算術,這幾乎是通用的。) –

+0

感謝Jim和Darshan。你們兩位都非常有幫助。 – sourabh912

4

輸出取決於格式字符的符號性,而不是聲明的符號性。仔細想想:printf無法知道x是否被聲明爲int或unsigned int,因爲C中沒有傳入類型信息。因此,它會根據打印的方式進行打印。 %d是有符號的,所以你得到一個有符號的值。用s.c,它是一個int,所以它被簽名,但是你用%u打印它,所以它被視爲無符號。

至於SI,它是無符號的,所以5可以裝配在其3位,所以它被傳遞給printf 5無符號延伸,所以%d(或%U)打印它作爲5.

+0

如果你說的是真的,那麼不應該輸出printf(「s.i =%d \ n」,s.i); – sourabh912

+0

你沒有完成你的想法,但我在我的編輯中回答。關於它沒有「如果」 - 這是所有C程序員都能理解的基本問題,他們不僅僅是像你一樣開始。 –

+0

對不起,我的互聯網連接有問題,所以不能編輯我的評論。其實我是問,不應該輸出printf(「s.i =%d \ n」,s.i);是-3(因爲我的格式說明符是%d,「i」僅存儲在3個位中,因此應將5(101)視爲負數)。 – sourabh912