算術轉換與整形提升
就在前天,我做了一道這樣的題,它使我發現我對數值類型轉換這裏的知識很膚淺。所以我整理了一下這方面的知識。
下面我做測試的平臺是VS 2013 32位環境(小端字節序)。
這是原題:
int main()
{
char u = 128;
unsigned char s = 128;
unsigned short us;
us = s + u;
printf("us = 0x%x\n", us);
us = (char)s + u;
printf("us = 0x%x\n", us);
us = (unsigned char)u + s;
printf("us = 0x%x\n", us);
return 0;
}
算術轉換
C語言在處理不同類型數值之間的運算時是有一定的規律的。需要先將兩個操作數轉換爲相同的類型才能進行運算。而類型的轉換的規律如下圖:
int
unsigned int
long int
unsigned long int
float
double
long double
這裏的轉換是自動由低向高發生的(當然,強制類型轉換是根據用戶意願進行的),例如:int和unsigned int進行運算,編譯器會將低級別的int轉換爲unsigned int再進行計算。而這個轉換編譯器並沒有對它在內存中的值做出改變,而是使用了不同的讀取方式,編譯器將原本的int的符號位也當成數值大小進行讀取。。
而這裏沒有提到的char和unsigned char,short和unsigned short類型在運算時都會自動爲‘升級’爲int型再進行運算。也就是我們所說的整形提升
整形提升
相應的,不同的低級別的類型向高級別類型整形提升的過程也是不太一樣的。
這裏的轉換的規律是:
unsigned char在轉化時,編譯器會將他們當成正數進行轉化,在高位補0,補全後就是它的補碼
signed char轉化時,編譯器則會根據他們的正負將他們的高位全部補0或補1,補全後就是它的補碼
我們再返回來看開頭我遇到的那個題:
int main()
{
char u = 128;
unsigned char s = 128;
unsigned short us;
us = s + u;
printf("us = 0x%x\n", us);
return 0;
}
u是signed char型的,在與類型爲unsigned char型的s進行加法運算時需要進行類型提升,提升爲int型,
因爲在u進行初始化時,將128賦值給了u,而signed char的取值範圍是-128 ~ 127,這裏的127的原碼是0111 1111,加一就是1000 0000,也就是-128,所以其實在u中存放的是-128這個數。-128的補碼也是1000 0000
那麼在類型提升時,編譯器會將它當作負數進行補全,高位補1,它在內存中即是0xffff80
而s是unsigned char型的,在提升時,編譯器認爲它是正數,所以高位補0,它在內存中爲0x000080。
00000000 00000000 00000000 10000000 //s的補碼 無符號相當於正數,原反補一樣,提升爲整型時,高位補0,
11111111 11111111 11111111 10000000 //u提升爲整型的補碼 u是signed char
+ -----------------------------------
10000000 00000000 00000000 00000000
它倆相加後,結果爲:0x80000000,在賦值unsigned short的us時將結果的低位兩個字節(16位)截斷賦值給us,所以us中保存的是0x0000
再看下面兩個語句:
int main()
{
char u = 128;
unsigned char s = 128;
unsigned short us;
us = (char)s + u;
printf("us = 0x%x\n", us);
return 0;
}
這裏將s先強制類型轉換爲signed char型,再進行運算。
而s一旦轉換爲signed char型,它的最高位就會表示符號位,其表示的值就會變爲-128,在提升爲整型時與u就有了相同的待遇。
所以這裏u和s的補碼都爲0xffff80,它倆的相加結果如下:
11111111 11111111 11111111 10000000 //u提升爲整型的補碼 u是signed char
11111111 11111111 11111111 10000000 //s被強制類型轉換後,與u有了相同的待遇
+ -----------------------------------
11111111 11111111 11111111 00000000
最終編譯器將相加結果截取低位的16位(兩個字節)存放到了unsigned short型的us中。這時us中存放的值是0xff00
最後我們再看剩下兩條語句:
int main()
{
char u = 128;
unsigned char s = 128;
unsigned short us;
us = (unsigned char)u + s;
printf("us = 0x%x\n", us);
return 0;
}
先將u強制類型轉換爲unsigned char,此時u的最高位符號位變爲數值大小,u的值變爲128,進行整型提升時,u被編譯器當作正數,高位補0,提升後,u在內存中表示爲0x000080
同樣的s類型爲unsigned char,整型提升後在內存中表示爲0x000080,兩者相加:
00000000 00000000 00000000 10000000 //u強制類型轉換爲unsigned char類型後整型提升
00000000 00000000 00000000 10000000 //s作爲unsigned char類型的整型提升
+ -----------------------------------
00000000 00000000 00000001 00000000
最終編譯器將相加結果截取低位的16位(兩個字節)存放到了unsigned short型的us中。這時us中存放的值是0x0100
我們通過在vs2013下運行這段代碼查看最終結果如下:
如果我的文章裏的解答有問題,希望小夥伴們積極指出