標籤:
接下來是不同類型之間的強制轉換。當把一個高容量的類型強制轉換為低容量的類型時,會發生截斷:丟棄二進位的高位,只保留低位(二進位的左邊為高位,右邊為低位);而把低容量類型強制轉換成高容量類型時,會發生擴充:在二進位的高位左邊繼續填充數字。擴充分為兩類:零擴充和符號擴充。下面看程式碼範例。
1、截斷
#include <stdio.h>int main(void){ short a = 257; char b = (char)a; printf("a = %u, b = %d\n", a, b); printf("a = %#x, b = %#x\n", a, b); return 0;}
結果:
a = 257, b = 1a = 0x101, b = 0x1
0x0101被分成兩部分,高位的01和低位的01,char類型只能容納一個位元組,於是取低位的一個位元組後,結果是0x01。
再看一個:
#include <stdio.h>int main(void){ short a = -24368; char b = (char)a; printf("a = %d, b = %d\n", a, b); printf("a = %#x, b = %#x\n", a, b); printf("a: %d, b: %d\n", sizeof a, sizeof b); return 0;}
結果:
a = -24368, b = -48a = 0xffffa0d0, b = 0xffffffd0a: 2, b: 1
雖然截斷後的結果是一致的,但是這裡的printf似乎是按四個位元組來列印負數的十六進位。
2、擴充
#include <stdio.h>int main(void){ unsigned short a = 63222u; unsigned int b = (unsigned int)a; printf("a = %d, b = %d\n", a, b); printf("a = %#x, b = %#x\n", a, b); printf("a: %d, b: %d\n", sizeof a, sizeof b); return 0;}
結果:
a = 63222, b = 63222a = 0xf6f6, b = 0xf6f6a: 2, b: 4
顯然,雖然列印出來的a和b的二進位表示相同,但是用sizeof運算子計算長度時,發現b佔用了4個位元組,所以b的實際十六進位為:0x0000f6f6。這表明,無符號數的擴充採用0來填充高位,這就是所謂的零擴充。
另外,我發現printf函數列印十六進位時,自動忽略高位的0。
有符號數呢?
#include <stdio.h>int main(void){ char a = -68; short b = (short)a; printf("a = %d, b = %d\n", a, b); printf("a = %#x, b = %#x\n", a, b); printf("a: %d, b: %d\n", sizeof a, sizeof b); return 0;}
結果:
a = -68, b = -68a = 0xffffffbc, b = 0xffffffbca: 1, b: 2
呃,又被嚇到了,我不禁懷疑printf函數列印十六進位時存在bug:sizeof顯示a的確只佔一個位元組,所以十六進位只列印0xbc就夠了,為毛還要加上前面一堆f?同理,b的話,0xffbc就夠了。。
不過至少,我們知道低容量的char向高容量的short擴充時,如果其二進位最高位是1,那麼擴充後的高位用1來填充,這就是所謂的符號擴充。
我再驗證一下有符號的正數:
#include <stdio.h>int main(void){ char a = 68; int b = (int)a; printf("a = %d, b = %d\n", a, b); printf("a = %#x, b = %#x\n", a, b); printf("a: %d, b: %d\n", sizeof a, sizeof b); return 0;}
結果:
a = 68, b = 68a = 0x44, b = 0x44a: 1, b: 4
恩,雖然也是用0來填充高位,但此時0應被視為正號,仍是符號擴充。
總結上面printf函數的這個小“bug”讓我不禁這樣猜想:是不是在做類型轉換時,無論是截斷還是擴充,先統一變成四個位元組,若不足四個位元組,則採用相應的擴充。
若列印十進位,則從右至左讀取相應的類型長度,然後停止讀取並將讀取的資料列印成十進位;但是列印十六進位時,在讀取了足夠的類型長度後,若讀到0則停止讀入,若讀到1則繼續讀入,直到讀夠4個位元組。
那麼最後,符號轉換和類型轉換一起上會怎樣?
#include <stdio.h>int main(void){ short a = -1; int b = (int)(unsigned short)a; unsigned int c = (unsigned)(int)a; printf("a = %d, a = %#x\n", a, a); printf("b = %d, b = %#x\n", b, b); printf("c = %u, c = %#x\n", c, c); //printf("a: %d, b: %d\n", sizeof a, sizeof b); return 0;}
結果:
a = -1, a = 0xffffffffb = 65535, b = 0xffffc = 4294967295, c = 0xffffffff
對於b,是先把a由signed short轉換成unsigned short,再轉換成int;而c的話,先做類型轉換,再由signed轉換成unsigned。我們看到結果是不同的,理由在上面說過了。另外,unsigned等價於unsigned int。
現在我們來研究一下有符號數和無符號數運算的問題。
C語言規定,如果參與運算的兩個數一個是有符號的另一個是無符號的,那麼有符號數會被隱式的強制轉換成無符號數,並假設兩個數都是非負數。
這條規則對於算數運算來說,不會導致什麼區別,也就是沒什麼負面作用。然而對於邏輯運算可能會產生意外。
如:-1 < 0u
表面上看沒什麼錯,但這個運算的結果是0,即false。。因為-1被轉換成無符號數後就是4294967295u,它當然不可能小於0u。。。
在比如:2147483647u > -2147483647 - 1
結果也是錯的,因為-2147483648轉換成無符號數後是2147483648。
C語言筆記之資料類型(二)