簡體   English   中英

編譯器在將有符號變量轉換為更大的變量類型時會使用什么算法,C 語言?

[英]What is the algorithm that a compiler would use while casting signed variables to larger variable types, C language?

答案可能取決於編譯器,但是;

以下行的預期 output 是什么?

signed char a = -5;
printf("%x \n", (signed short) a); 
printf("%x \n", (unsigned short) a);

編譯器會在將signed char轉換為更大的變量時用零 (0) 或一 (1) 填充最高有效位嗎? 如何以及何時?


PS 還有其他問題。 我試圖在在線編譯器上運行下面的代碼進行測試。 結果並不像我預期的那樣。 所以我添加了詳細的轉換,但它沒有用。 為什么printf("%x \n", (signed char)b);的output是 4 個字節長而不是 1 個字節?

int main()
{
    unsigned char a = (unsigned char)5;
    signed char b = (signed char)-5;
    
    unsigned short c;
    signed short d;
    
    c = (unsigned short)b;
    d = (signed short)b;
    
    printf("%x ||| %x ||| %x ||| %x\n", (unsigned char)a, (signed char)b, c, d);
    printf("%d ||| %d ||| %d ||| %d\n", a, b, c, d);
    printf("%d ||| %d ||| %d ||| %d\n", a, b, (signed char)c, (signed char)d);

    return 0;
}


Output:

5 ||| fffffffb ||| fffb ||| fffffffb
5 ||| -5 ||| 65531 ||| -5   
5 ||| -5 ||| -5 ||| -5

在 C 中,arguments 到等級低於int的可變參數函數(如printf )被轉換為int (不是unsigned int除非參數是無符號的並且寬度與int相同)。

signed shortsigned char轉換為signed int不會更改值。 如果您從 -5 開始,您將以 -5 結束。

但是,如果您將負符號值轉換為無符號類型(例如,使用顯式強制轉換),則轉換將以比無符號類型的最大值大一為模的方式完成。 例如, unsigned short的最大值為 65535(在許多實現中),因此將 -5 轉換為unsigned short結果為 -5 模 65536,即 65531。(C 的%運算符不產生數學模歸約。)當那個然后 value 被隱式轉換為int ,它仍然是 65531,所以這就是用%x ( fffb ) 打印的內容。

請注意,將格式%x應用於signed int在技術上是不正確的。 %x要求相應的參數是一個unsigned int 目前,C 不保證將有符號值解釋為無符號值的結果是什么,但這很快就會改變。 (這不是轉換。在運行時,類型不再存在,值只是位模式。)

C11 標准的第 6.3.1.3 節中列出了在有符號和無符號類型之間轉換的確切規則:

1當 integer 類型的值轉換為_Bool以外的另一種 integer 類型時,如果該值可以用新類型表示,則它不變。

2否則,如果新類型是無符號的,則通過比新類型可以表示的最大值重復加或減一來轉換值,直到該值在新類型的范圍內。

3否則,新類型已簽名,無法在其中表示值; 結果是實現定義的,或者引發了實現定義的信號。

至於上面這段代碼的含義:

signed char a = -5;
printf("%x \n", (signed short) a); 
printf("%x \n", (unsigned short) a);

這里發生了一些事情。

對於第一個printf ,您首先將signed char轉換為signed short 根據上面的第 1 條,由於值 -5 可以存儲在兩者中,因此值不會被強制轉換更改。 然后,因為這個值被傳遞給可變參數 function,所以它被提升為int類型,並且再次通過第 1 條,該值保持不變。

然后使用%x格式說明符打印生成的int值,該說明符需要一個unsigned int 對於不匹配的格式說明符,這在技術上是未定義的行為,盡管大多數實現將允許隱式簽名/未簽名重新解釋。 因此,假設二進制補碼表示,將打印int值 -5 的表示,並假設 32 位int這將是fffffffb

對於第二個printf ,從signed charunsigned short的轉換將根據上面的第 2 條發生,因為值 -5 不能存儲在unsigned short中。 假設 16 位短,這給你值 65536 - 5 = 65531。假設兩個補碼表示,這相當於將表示從fb符號擴展到fffb 這個unsigned short值然后在傳遞給printf時被提升為int ,並且根據第 1 條,該值保持不變。 然后%x格式說明符將其打印為fffb

當被轉換的值可以在目標類型中表示時,integer 類型之間的轉換是值保留的。 signed short可以表示signed char可表示的所有值,所以這...

signed char a = -5;
printf("%hd\n", (signed short) a);

...預計 output 包含“-5”的行。

但是,您的代碼具有未定義的行為。 轉換說明符%x要求相應的參數具有類型unsigned int ,而您傳遞的是帶signed short (根據默認參數促銷轉換為int )。

如果您的實現對有符號整數使用二進制補碼表示(我可以肯定地斷言它確實如此),則表示會將原始帶signed char符號擴展為帶signed short的寬度,然后將符號擴展為(signed) int的寬度。 因此,UB 在您身上的一種合理可能的表現形式……

 printf("%x \n", (signed short) a);

...將是打印

fffffffb

另一種情況有點不同。 Integer 目標類型為無符號且不能表示源值的轉換已明確定義。 通過以目標類型中可表示值的數量為模減少源值,將源值轉換為目標類型。 因此,如果您的unsigned short有 16 個值位,那么將 -5 轉換為unsigned short的結果是 -5 modulo 65536,即 65531。

因此,

printf("%hu\n", (unsigned short) a);

預計會打印包含“65531”的行。

同樣, %x轉換說明符與相應參數的類型不匹配( (unsigned short) a ,通過默認參數提升轉換為int ),因此您的printf具有未定義的行為。 但是,在二進制補碼系統上將 16 位unsigned short轉換為 32 位int將涉及零擴展源的表示形式,因此 UB 在您的...

 printf("%x \n", (unsigned short) a);

...將是打印

fffb

.

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM