簡體   English   中英

將無符號整數轉換為有符號整數 C

[英]Convert unsigned int to signed int C

我正在嘗試將65529unsigned int轉換為 signed int 我試着做這樣的演員:

unsigned int x = 65529;
int y = (int) x;

但是y應該返回 -7 時仍然返回 65529。 這是為什么?

您似乎希望intunsigned int是 16 位整數。 顯然情況並非如此。 最有可能的是,它是一個 32 位整數 - 它足夠大,可以避免您期望的環繞。

請注意,沒有完全符合 C 的方法來執行此操作,因為對於超出范圍的值在有符號/無符號之間進行轉換是實現定義的。 但這在大多數情況下仍然有效:

unsigned int x = 65529;
int y = (short) x;      //  If short is a 16-bit integer.

或者:

unsigned int x = 65529;
int y = (int16_t) x;    //  This is defined in <stdint.h>

我知道這是一個老問題,但這是一個好問題,那么這個呢?

unsigned short int x = 65529U;
short int y = *(short int*)&x;

printf("%d\n", y);

@Mysticial 明白了。 一個簡短的通常是 16 位,將說明答案:

int main()  
{
    unsigned int x = 65529;
    int y = (int) x;
    printf("%d\n", y);

    unsigned short z = 65529;
    short zz = (short)z;
    printf("%d\n", zz);
}

65529
-7
Press any key to continue . . .


再詳細一點。 這完全與帶符號的數字如何存儲在內存中有關。 搜索二進制補碼符號以獲取更多詳細信息,但這里是基礎知識。

所以讓我們看看 65529 十進制。 它可以用十六進制表示為FFF9h 我們也可以用二進制表示:

11111111 11111001

當我們聲明short zz = 65529; ,編譯器將 65529 解釋為有符號值。 在二進制補碼表示法中,最高位表示有符號值是正數還是負數。 在這種情況下,您可以看到最高位是1 ,因此它被視為負數。 這就是它打印出-7的原因。

對於unsigned short ,我們不關心 sign ,因為它是unsigned 因此,當我們使用%d打印出來時,我們使用所有 16 位,因此它被解釋為65529

要了解原因,您需要知道 CPU 使用二進制補碼(可能不是全部,但很多)來表示有符號數。

    byte n = 1; //0000 0001 =  1
    n = ~n + 1; //1111 1110 + 0000 0001 = 1111 1111 = -1

而且,類型 int 和 unsigned int 的大小可以根據您的 CPU 不同而不同。 在做這樣的特定事情時:

   #include <stdint.h>
   int8_t ibyte;
   uint8_t ubyte;
   int16_t iword;
   //......

對於 16 位整數,值 65529u 和 -7 的表示是相同的。 只是位的解釋不同。

對於較大的整數和這些值,您需要對擴展進行簽名; 一種方法是邏輯運算

int y = (int )(x | 0xffff0000u); // assumes 16 to 32 extension, x is > 32767

如果速度不是問題,或者處理器上的除法速度很快,

int y = ((int ) (x * 65536u)) / 65536;

乘法左移 16 位(再次假設擴展為 16 到 32 位),而除法右移保持符號不變。

由於轉換無符號值用於表示正數,因此可以通過將最高有效位設置為 0 來完成轉換。因此程序不會將其解釋為二進制補碼值。 一個警告是,這將丟失接近無符號類型最大值的數字的信息。

template <typename TUnsigned, typename TSinged>
TSinged UnsignedToSigned(TUnsigned val)
{
    return val & ~(1 << ((sizeof(TUnsigned) * 8) - 1));
}

我知道這是一個老問題,但我認為響應者可能誤解了它。 我認為其目的是將作為無符號整數(技術上, unsigned short )接收到的 16 位位序列轉換為有符號整數。 當您需要將從網絡接收到的某些內容從網絡字節順序轉換為主機字節順序時,可能會發生這種情況(最近對我來說是這樣)。 在這種情況下,使用聯合:

unsigned short value_from_network;
unsigned short host_val = ntohs(value_from_network);
// Now suppose host_val is 65529.
union SignedUnsigned {
  short          s_int;
  unsigned short us_int;
};
SignedUnsigned su;
su.us_int = host_val;
short minus_seven = su.s_int;

現在minus_seven的值為-7。

您期望您的int類型為 16 位寬,在這種情況下您確實會得到一個負值。 但很可能它是 32 位寬,所以一個有符號的int可以代表 65529 就好了。 您可以通過打印sizeof(int)來檢查這一點。

要回答上面評論中發布的問題 - 嘗試這樣的事情:

unsigned short int x = 65529U;
short int y = (short int)x;

printf("%d\n", y);

或者

unsigned short int x = 65529U;
short int y = 0;

memcpy(&y, &x, sizeof(short int);
printf("%d\n", y);

暫無
暫無

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

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