2008-09-08 73 views
103

假設我有以下C代碼。在C中籤名爲無符號轉換 - 它總是安全嗎?

unsigned int u = 1234; 
int i = -5678; 

unsigned int result = u + i; 

什麼的隱式轉換這裏發生了,而且是這個代碼安全起見ui所有值? (安全,在這個意義上,即使結果在這個例子中會溢出一些巨大的正數,我可以將它轉換回一個INT並獲得真正的結果。)

回答

176

簡答

i你將轉換爲無符號整數通過添加UINT_MAX + 1,然後加入將與無符號值進行,導致大result(取決於ui和的值)。

長的答案

按照C99標準:

6.3.1.8常見的算術轉換

  1. 如果兩個操作數具有相同的類型,則不需要進一步的轉換。
  2. 否則,如果兩個操作數已簽署整數類型或兩者都具有的無符號整數類型,具有較小整數轉換等級的類型的操作數轉換爲操作數的具有更大的秩的類型。
  3. 否則,如果具有無符號整數類型的操作數的秩大於或等於另一個操作數的類型的秩,然後用帶符號的整數類型的操作數被轉換成無符號整數類型的操作數的類型。
  4. 否則,如果用符號整型操作數的類型,可以表示所有與無符號整數類型的操作數的類型的值,則與無符號整數類型的操作數轉換爲操作數的類型與符號整數類型。
  5. 否則,兩個操作數都轉換爲與帶符號整數類型的操作數的類型相對應的無符號整數類型。

在你的情況,我們有一個unsigned int類型(u)和符號int(i)。參照上面(3),由於兩個操作數具有相同的等級,所以你的i將需要被轉換爲爲無符號整數。

6.3.1.3符號和無符號整數

  1. 當與整數類型的值被轉換爲比其它_Bool另一個整數類型,如果該值可以通過新的類型來表示,它是不變的。
  2. 否則,如果新類型是無符號的,則該值是通過重複地加上或減去小於能夠在新的類型來表示,直到該值是在新的類型的範圍的最大值一個更轉換。
  3. 否則,新類型被簽名並且其值不能被表示;結果是實現定義的或實現定義的信號被引發。

現在我們需要參考上面的(2)。您的i將通過添加UINT_MAX + 1轉換爲無符號值。所以結果將取決於您的實施如何定義UINT_MAX。這將是大的,但它不會溢出,因爲:

6.2.5(9)

涉及無符號的操作數的一種計算可以永遠不會溢出,因爲不能由所得到的無符號整數表示的結果類型被減少的模數大於可由最終類型表示的最大值的數。

獎勵:算術轉換半WTF

#include <stdio.h> 

int main(void) 
{ 
    unsigned int plus_one = 1; 
    int minus_one = -1; 

    if(plus_one < minus_one) 
    printf("1 < -1"); 
    else 
    printf("boring"); 

    return 0; 
} 

你可以使用這個鏈接來試試這個在線:http://codepad.org/yPhYCMFO

獎勵:算術轉換副作用

算術轉換規則可用於獲取的值0通過初始化一個無符號值到-1,即:

unsigned int umax = -1; // umax set to UINT_MAX 

這保證是便攜式無論由於上述的轉換規則的系統的符號數表示的。看到這個問題的更多信息:Is it safe to use -1 to set all bits to true?

+11

哇那裏。它的定義是從簽名到未簽名,但是從無符號到簽名是由實現定義的。 – rlbond 2009-07-18 17:00:34

+5

這是不正確的。從語言的角度來看,從'int'到'unsigned int'的整數轉換與源對象的值有關,與其內部表示無關(概念上)。該值使用模2^N算法進行轉換,其中N是「unsigned int」中值的位數,無論表示實現用於「int」。 – 2010-07-08 12:03:39

+0

這個答案根本不對。它解釋了常見的實現是如何工作的,而不是語言如何工作。 – 2010-08-07 18:18:14

3

當一個無符號和一個有符號變量被添加(或任何二進制操作)都被隱式轉換爲無符號,這在這種情況下會導致巨大的結果。

因此,它的結果可能是巨大的和錯誤的,但它永遠不會崩潰。

3

從signed到unsigned的轉換有兩種可能性。最初爲正值的數字仍然(或被解釋爲)相同的值。現在被解釋爲更大的正數。

1

正如之前所回答的,您可以在有問題和無問題之間來回轉換。有符號整數的邊界大小寫是-1(0xFFFFFFFF)。嘗試添加和減去,你會發現你可以退回並保持正確。

然而,如果你將要鑄造來回,我會強烈建議命名變量,使得它清楚自己是什麼類型的,如:

int iValue, iResult; 
unsigned int uValue, uResult; 

這是太容易得到被更重要的問題分心,並忘記哪個變量是什麼類型,如果他們沒有提示命名。您不希望轉換爲無符號數,然後將其用作數組索引。

3

參照the bible

  • 你的加法操作導致要轉換爲無符號的int INT。
  • 假設二進制補碼錶示和相同大小的類型,位模式不會改變。
  • 從unsigned int到signed int的轉換依賴於實現。 (但是它可能按照你現在在大多數平臺上的預期方式工作。)
  • 在結合帶符號和無符號大小不同的情況下,規則稍微複雜一些。從
16

轉換符號到無符號不不一定只是複製或重新解釋的符號值的表示。引述C標準(C99 6.3.1.3):

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

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

對於近來普遍使用的二進制補碼錶示,規則確實對應於重新解釋這些位。但對於其他表示(符號和大小或補碼),C實現必須仍然安排相同的結果,這意味着轉換不能只複製位。例如,(無符號)-1 == UINT_MAX,無論表示如何。

通常,C中的轉換被定義爲對值進行操作,而不是對錶示進行操作。

要回答原來的問題:

unsigned int u = 1234; 
int i = -5678; 

unsigned int result = u + i; 

i的值被轉換成無符號整型,得到UINT_MAX + 1 - 5678。然後將該值添加到無符號值1234,產生UINT_MAX + 1 - 4444

(不同於無符號溢出,溢出簽訂調用未定義行爲環繞式是常見的,但不是由C標準保證 - 和編譯器優化可以在代碼,使無根據的假設肆虐。)

-15

可怕的答案嘉豪

Özgür的Ozcitak

當您從符號到無符號 投(和反之亦然)內部 表示的號碼不會 更改。 編譯器如何解釋符號位。

這是完全錯誤的。

墊弗雷迪克森

當一個無符號和一個簽署 變量被添加(或任何二進制 操作)都是隱式 轉換爲無,這將在 這種情況下結果在一個巨大的結果。

這也是錯誤的。由於無符號類型中的填充位,無符號整數可以被提升爲整數,因爲它們具有相等的精度。

SMH

你的加法運算引起INT 轉換爲一個unsigned int。

錯誤。也許它確實,也許它沒有。

從無符號整數轉換爲有符號的 int取決於實現。 (但 它可能運作的,你希望 在大多數平臺上,這些天的方式。)

錯誤。如果它導致溢出或值被保留,它可以是未定義的行爲。

匿名

i的值被轉換爲 無符號整型...

錯誤。取決於int相對於unsigned int的精度。

泰勒價格

正如前面得到的回答是,你可以 投來回 無符號簽署沒有問題。

錯誤。試圖存儲超出有符號整數範圍的值會導致未定義的行爲。

現在我終於可以回答這個問題。

如果int的精度等於unsigned int,則u將被提升爲帶符號的int,並且您將從表達式(u + i)中獲取值-4444。現在,如果你和我有其他的值,你可能會發生溢出和未定義的行爲,但確切的數字,你會得到-4444 [1]。該值將具有類型int。但是你試圖將這個值存儲到一個無符號整型中,這樣就會被轉換爲一個無符號整數,並且結果的值將會是(UINT_MAX + 1)-4444。

如果無符號的精度int大於int的值,signed int將被提升爲一個unsigned int,產生值(UINT_MAX + 1) - 5678,它將被添加到另一個unsigned int 1234.如果你和我有其他的值,表達式落在範圍{0..UINT_MAX}之外時,值(UINT_MAX + 1)將被添加或減去,直到結果DOES落在範圍{0..UINT_MAX)內且不會發生未定義的行爲。

什麼是精度?

整數具有填充位,符號位和值位。無符號整數顯然沒有符號位。無符號字符進一步保證沒有填充位。一個整數的值的位數是它的精度。

[陷阱]

宏的sizeof宏不能單獨被用來確定一個整數的精度,如果填充比特都存在。並且字節的大小不一定是由C99定義的八位字節(八位)。

[1]溢出可能發生在兩點之一。在添加之前(在提升期間) - 當你有一個unsigned int,這個int太大而不能放入int。即使unsigned int在int範圍內,加法後溢出也可能發生,加法後結果可能仍然溢出。


在一個不相關的說明,我是一個剛畢業的學生試圖找到工作;)

0

什麼隱式轉換是怎麼回事,

我會被轉換爲無符號整數。

並且此代碼對您和我的所有值都是安全的嗎?

在明確定義的意義上是安全的(見https://stackoverflow.com/a/50632/5083516)。

規則的寫入通常很難讀取標準說話,但基本上在有符號整數中使用任何表示形式,無符號整數將包含數字的2的補碼錶示形式。

加法,減法和乘法將在這些數字上正確工作,從而產生另一個無符號整數,其中包含表示「真實結果」的二進制補碼數字。

除法和轉換爲較大的無符號整數類型將具有明確定義的結果,但這些結果不會是「實際結果」的2的補碼錶示。

(安全,即使這個例子中的結果會溢出到一些巨大的正數,我可以將它轉換回int並獲得真正的結果。)

雖然從轉換符號到無符號由標準的反向定義是實現定義的GCC和MSVC定義轉換,這樣你會得到「真正的結果」時,將儲存在2的補數無符號整數回到有符號整數。我希望你只能在晦澀的系統上發現任何其他行爲,這些行爲不使用2作爲有符號整數的補碼。

https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation https://msdn.microsoft.com/en-us/library/0eex498h.aspx

相關問題