2013-08-04 42 views
3

我必須參考這個頁面stdint.h:stdint.h中的無符號整數類型對可存儲內容沒有限制?

http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/stdint.h.html 

我完全驚訝地看到,我可以定義爲uint64_t中類型 變量中存儲負數。我的想法是,我的腦海裏一個無符號的 數據類型總是一個正數,如果我試圖在那裏存儲一個 負數,我會得到一個錯誤。下面讓我大吃一驚:

#define _XOPEN_SOURCE 600 

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <stdint.h> 
#include <stddef.h> 
#include <errno.h> 

int main (int argc, char *argv[]) { 

    uint64_t a_big_positive_number; 
    unsigned long another_big_positive_number; 

    if (argc < 2) { 
     printf ("usage : %s some_integer\n", argv[0]); 
     exit(EXIT_FAILURE); 
    } 

    a_big_positive_number = 0; 

    a_big_positive_number = strtol(argv[1], (char **)NULL, 10); 
    fprintf (stderr, "%i : %s\n", errno, strerror(errno)); 
    another_big_positive_number = a_big_positive_number; 
    fprintf (stderr, "%i : %s\n", errno, strerror(errno)); 

    return (EXIT_SUCCESS); 

} 

當我編譯的全C99迂腐的標誌,我得到一個二進制文件做了 以下驚人的事情:

$ ./boffo -12345 
0 : Error 0 
0 : Error 0 

真的嗎?

我跑了一個調試器中,我看到的確是的,uint64_t中是完全 精細與具有負數和近,我可以告訴我可以在那裏stuiff任何 64位我想:

(dbx) run -12345 
Running: boffo -12345 
(process id 16270) 
stopped in main at line 15 in file "boffo.c" 
    15  if (argc < 2) { 
(dbx) step  
stopped in main at line 20 in file "boffo.c" 
    20  a_big_positive_number = 0; 

首先,我想看看在內存中(在棧上我猜的)這個變種是:

(dbx) print &a_big_positive_number 
&a_big_positive_number = 0xffffffff7ffff620 

大......現在得到的argv [1]輸入字符串,並將其轉換爲長整型:

(dbx) step       
stopped in main at line 22 in file "boffo.c" 
    22  a_big_positive_number = strtol(argv[1], (char **)NULL, 10); 
(dbx) step 
stopped in main at line 23 in file "boffo.c" 
    23  fprintf (stderr, "%i : %s\n", errno, strerror(errno)); 

我們現在在那個地址有什麼記憶?

(dbx) x &a_big_positive_number/4 x 
0xffffffff7ffff620:  0xffff 0xffff 0xffff 0xcfc7 

無符號的64位數據類型內負數。根本沒有錯誤!

(dbx) cont       
Reading libc_psr.so.1 
0 : Error 0 
0 : Error 0 

execution completed, exit code is 0 
(dbx) quit 

所以現在的問題是,什麼是目的或 stdint.h內的無符號整數的值時,它好像我可以用一個64位的位字段或排列的8個無符號的字符 或任何一塊內存足夠大?價值 在哪裏?可移植性?從鄉親

/************** COMMENT AFTER SEEING GREAT ANSWERS BELOW ************/ 

偉大的答案下面不吝我,我看到stdint.h類型 可以跨平臺移植,他們允許正 整數值的更大範圍。存儲在變量中的數據的解釋是 ,這是我需要處理的事情,不要指望我的編譯器或調試器神祕地 或神奇地知道我們只在這裏使用正數。

其實,我認爲最安全的做法是堅持使用uint32_t的是在 C99標準,然後做在那裏得到一個正確的正值:

a_big_positive_number = (uint32_t) labs(strtol(argv[1], (char **)NULL, 10)); 

是超級「帶和吊帶「安全我應該在我接受 之前自己檢查argv [1]。誰知道,這可能是負面的,或者它可能是垃圾數據。

回答

5

將有符號整數類型轉換爲無符號整數類型在C中完全定義,並在a_big_positive_number = strtol(…);中隱式發生,就像您已寫入a_big_positive_number = (uint64_t) strtol(…);一樣。

C99標準的6.3.1.3:2子句適用:

否則,如果新類型是無符號的,則該值是通過重複地加上或減去比可以是最大值多一個轉換以新類型表示,直到值處於新類型的範圍內。

(在從另一種類型的轉換的上下文中)

而6.5.16.1:2子句說,轉換髮生在分配:

在簡單賦值(=),該值的右操作數被轉換爲賦值表達式的類型,並替換左操作數指定的對象中存儲的值。

關於價值uint64_t,我不確定你在問什麼。類型uint64_t是一個正好64位的無符號類型。一個unsigned long long bf:64;位字段不會提供任何內存中只有64位的保證,也不會提供八個無符號字符的數組(並且操作符不會直接在後者上工作)。

+0

將有符號整數類型轉換爲無符號整數類型?但是沒有轉換。我在uint64_t類型對象中仍然有一個負數,調試器輸出證實了這一點。我看不到有任何「轉換」發生。 –

+2

@paullanken「消極」或「正面」和「有符號」和「無符號」(以及一般類型) - 這些只是慣例,由編譯器和程序員在高層使用。問題是你的號碼將包含一些位模式,不管它的類型是什麼。現在您可以將其解釋爲有符號或無符號,並且您將看起來會得到不同的結果 - 但這實際上只是您想如何解釋由變量表示的原始內存內容的問題。 – 2013-08-04 18:49:41

+3

@paullanken如果在調試器中將無符號變量的內容作爲有符號整數進行打印,您將獲得一個有符號值。調試器不知道符號的類型,並使用您指示的類型來解釋內存的內容。 –

3

簽名/未簽名類型的原因不是存儲,而是關於如何解釋位模式。舉個例子:

long signedNumber = -1l; 
assert(signedNumber < 0); //Bit pattern 0xffffffffffffffff is interpreted as negative, so this succeeds. 
unsigned long unsigendNumber = (unsigned long)signedNumber; //same bit pattern ... 
assert(unsignedNumber >= 0ul); //... but interpreted as an unsigned, it's roughly 18e18. 

其實,加法,減法,乘法和是無符號和符號數相同,硬件並不關心。這只是爲了編譯器需要知道的比較等特殊操作,第一位是否必須被解釋爲符號位。

+0

再次,我只是看不到這裏的無符號數據類型的值,當我可以輕鬆地填入一個負數。沒有任何錯誤或任何警告標誌(errno?)告訴我你不應該那樣做。它看起來像stdint.h中的無符號數據類型是沒有價值的。我在這裏錯過了什麼? –

+2

問題是,當您使用無符號類型而不是簽名類型時,您有更多正數。 – cmaster

+2

一個無符號數給你兩倍的正範圍。它也有翻轉*明確定義*(有符號的數字沒有)。如果您需要它,請添加一些明確的錯誤檢查。也許你想允許/禁止0,該怎麼知道該語言?在有些情況下,這種無錯誤行爲是有益的,例如圖像處理,加密,查找限制等。 – Dave

相關問題