首先引出一個問題,這是我在編寫模擬器程式的時候遇到的,我們有如下的類型轉換:
int16_t v16s;
uint16_t v16u;
int32_t v32s;
uint32_t v32u;
v16s = 0xf08b; v16u = (uint16_t)v16s; //請問此時v16s和v16u在二進位表示形式上有什麼不同嗎? No.1
v32s = (int32_t)v16s; v32u = (uint32_t)v16s; //請問此時v32s和v32u在二進位表示形式上相同嗎? No.2
v16s = 0x0f8b; v32s=(int32_t)v16s; v32u=(uint32_t)v16s; //請問此時v32s和v32u的值又是多少? No.3
v32s = 0xffff0fb8; v16s=(int16_t)v32s; v16u=(uint16_t)v32s;//請問此時v16s和v16u的值是多少? No.4
v16s = 0xf08b; v16u=0xf08b; v32s=(int32_t)v16s; v32u=(uint32_t)v16u; //請問此時v32s和v32u的值是多少? No.5
首先告訴大家答案:
- No.1:請問此時v16s和v16u在二進位表示形式上有什麼不同嗎?
二者在二進位表示形式上沒有區別,都是0xf08b,所以如果不進行運算操作,在同樣長度的單一資料型別之間進行強制類型轉換是不必要的,編譯器會略去你的工作,不信的話,你可以查看產生的彙編代碼,對於No.1問題對轉換語句,根本就不產生代碼!!
- No2:請問此時v32s和v32u在二進位表示形式上相同嗎?
二者在二進位表示形式上是相同的!都是0xfffff08b!這裡可能會犯錯的地方在 v32u=(uint32_t)v16s這個語句上,我就是這麼犯的錯誤。按照我錯誤的理解是:0xf08b → 0x0000f08b!這是不對的!原因後面再告訴你(當然如果你C語言學的夠好,就覺得我再弄小兒科了^_^,照顧一下後進者)。
- No.3:請問此時v32s和v32u的值又是多少?
此時v32s和v32u的值都是 0x00000f8b!
- No.4:請問此時v16s和v16u的值是多少?
此時v16s和v16u的值都是0x0fb8!
- No.5:此時v32s的值是0xfffff08b,v32u的值是0x0000f08b。在進行自動類型轉換的時候,如果原來的數是無符號數,那麼在擴充的時候,高位填充的是0;如果是有符號數,那麼高位填充的時符號位!這一點有點類似於“>>”操作符,當無符號數右移的時候,高位填充的是0;有符號數右移的時候,高位填充的是符號位。
好了,下面我就來告訴你,為什麼是上面的結果。這就需要我們回頭複習一下C語言的有關類型轉換的操作。C語言的類型轉換,可以分為兩種:自動類型轉換(隱式類型轉換,有編譯器幫你去完成)和強制類型轉換(你知道自己想要什麼,所以才轉換)。
對於自動類型轉換,最常見的就是混合運算以及賦值運算,還有一種就是函數值的類型轉換
- 賦值運算:自動把“=”右邊的運算式的類型轉換成“=”右邊的變數的類型,例如 int a=4.5; a的值實際是4!
- 混合運算:就是一個運算運算式當中包含了多個類型,這時候就需要有類型轉換。當運算子兩邊的運算元類型不同時,其中一個運算元就要經過類型轉換以和另一個運算元的類型相一致,然後才能進行運算。
變換運算元採取就高不就低的原則,即層級低的運算元先被轉換成和層級高的運算元具有同一類型,然後再進行運算,結果的資料類型和層級高的運算元相同。
高 double ←← float
↑ ↑
↑ long
↑ ↑
↑ unsigned
↑ ↑
低 int ←← char,short
自動轉換順序表
- 函數傳回值的類型轉換: int f1(){ return 36.8;}
強制類型轉換運算子
可以利用強制類型轉換運算子將一個運算式轉換成所需類型:
例如:
(double)a (將a轉換成double類型)
(int)(x+y) (將x+y的值轉換成整型)
(float)(5%3) (將5%3的值轉換成float型)
(int)(1.5+2.3) = ?
(int)1.5+2.3=?
注意,運算式應該用括弧括起來。如果寫成(int)x+y 則只將x轉換成整型,然後與y相加。
講到這裡,您或許就明白了,哦,原來我們是使用了強制類型轉換的文法,但是程式實質上卻是自動類型轉換! 所以在執行了語句:v16s = 0xf08b; v32s=(int32_t)v16s; v32u=(uint32_t)v16s; 後v32s和v32u的二進位表示形式才是一樣的!
對於從高到低的強制轉換,實質上就是一個截斷的操作,只把低端需要的部分保留,其餘的部分直接扔掉了。所以對於語句: v32s = 0xffff0fb8; v16s=(int16_t)v32s; v16u=(uint16_t)v32s; 執行之後v16s和v16u的二進位表示形式才是一樣的!
註:我一直強調的都是它們的二進位表示形式是否相同,而沒有說它們的值是否相同,因為同樣的二進位表示形式,你把它當作有符號數和無符號數的值在決大部分的情況下都是不同的(除了最高位為0的相同外,其他都不同)
混合運算
混合運算是指在一個運算式中參與運算的對象不是相同的資料類型,例如:
2*3.1416*r 3.1416*r*r 3.6*a%5/(*b)+'f';
如果r為int型變數,a為float型變數,b為double型變數,則以上三個運算式中涉及到的資料類型有整型、實型、字元型,這種運算式稱為混合類型運算式。對混合類型運算式的求解要進行混合運算,此時首要的問題是對參與運算的資料進行類型轉換。
下面給出類型轉換的樣本,以加深理解。設有如下變數說明:
int a, j, y; float b; long d; double c;
則對指派陳述式:
y=j+'a'+a*b-c/d;
其運算次序和隱含的類型轉換如下:
① 計算a*b,由於變數b為float型,所以運算時先由系統自動轉換為double型,變數a為int型,兩個運算對象要保持類型一致,變數a也要轉換為double,運算結果為double型。
② 由於c為double型,將 d 轉換成 double 型,再計算 c/d,結果為double型。
③ 計算j+'a',先將'a'(char型)轉換成整型數再與j相加,結果為整型。
④ 將第1步和第3步的結果相加,先將第3步的結果(int)轉換成double型再進行運算,結果為double型。
⑤ 用第4步的結果減第2步的結果,結果為double型。
⑥ 給y賦值,先將第5步的結果double型轉換為整型(因為賦值運算左邊變數y為整型),即將double型資料的小數部分截掉,壓縮成int型,然後進行賦值。
以上步驟中的類型轉換都是C語言編譯系統自動完成的。