通常,在語句和表達式中應使用類型相同的變量和常量。但是,如果使用混合類型,最好先了解一些基本的類型轉換規則。
1 類型轉換規則
1.當類型轉換出現在表達式時,無論是unsigned還是signed的char和short都會被自動轉換成int,如有必要會被轉換成unsigned-int(如果short與int的大小相同,unsigned short就比int大。這種情況下,unsigned short會被轉換成unsigned int)。在K&R那時的C中,float會被自動轉換成double(目前的C不是這樣)。由于都是從較小類型轉換為較大類型,所以這些轉換被稱為升級(promotion)。
2.涉及兩種類型的運算,兩個值會被分別轉換成兩種類型的更高級別。
3.類型的級別從高至低依次是long-double、double、float、unsigned-long-long、long-long、unsigned-long、long、unsigned-int、int。例外的情況是,當long和int的大小相同時,unsigned-int比long的級別高。之所以short和char類型沒有列出,是因為它們已經被升級到int或unsigned-int。
4.在賦值表達式語句中,計算的最終結果會被轉換成被賦值變量的類型。這個過程可能導致類型升級或降級(demotion)。所謂降級,是指把一種類型轉換成更低級別的類型。
5.當作為函數參數傳遞時,char和short被轉換成int,float被轉換成double。
類型升級通常都不會有什么問題,但是類型降級會導致真正的麻煩。原因很簡單:較低類型可能放不下整個數字。例如,一個8位的char類型變量存儲整數101沒問題,但是存不下22334。
如果待轉換的值與目標類型不匹配怎么辦?這取決于轉換涉及的類型。待賦值的值與目標類型不匹配時,規則如下。
1.目標類型是無符號整型,且待賦的值是整數時,額外的位將被忽略。例如,如果目標類型是8位unsigned-char,待賦的值是原始值求模256。
2.如果目標類型是一個有符號整型,且待賦的值是整數,結果因實現而異。
3.如果目標類型是一個整型,且待賦的值是浮點數,該行為是未定義的。
2 浮點數的類型轉換
如果把一個浮點值轉換成整數類型會怎樣?當浮點類型被降級為整數類型時,原來的浮點值會被截斷。例如,23.12和23.99都會被截斷為23,-23.5會被截斷為-23。程序清單5.14演示了這些規則。
#include <stdio.h>
int main(void)
{
char ch;
int i;
float fl;
fl = i = ch = 'C';
/* line 9 */
printf("ch = %c, i = %d, fl = %2.2fn", ch, i, fl); /* line 10 */
ch = ch + 1;
/* line 11 */
i = fl + 2 * ch;
/* line 12 */
fl = 2.0 * ch + i;
/* line 13 */
printf("ch = %c, i = %d, fl = %2.2fn", ch, i, fl); /* line 14 */
ch = 1107;
/* line 15 */
printf("Now ch = %cn", ch);
/* line 16 */
ch = 80.89;
/* line 17 */
printf("Now ch = %cn", ch);
/* line 18 */
return 0;
}
運行convert.c后輸出如下:
ch = C, i = 67, fl = 67.00
ch = D, i = 203, fl = 339.00
Now ch = S
Now ch = P
在我們的系統中,char是8位,int是32位。程序的分析如下。
- 第9行和第10行:字符'C'被作為1字節的ASCII值存儲在ch中。整數變量i接受由'C'轉換的整數,即按4字節存儲67。最后,fl接受由67轉換的浮點數67.00。
- 第11行和第14行:字符變量'C'被轉換成整數67,然后加1。計算結果是4字節整數68,被截斷成1字節存儲在ch中。根據%c轉換說明打印時,68被解釋成'D'的ASCII碼。
- 第12行和第14行:ch的值被轉換成4字節的整數(68),然后2乘以ch。為了和fl相加,乘積整數(136)被轉換成浮點數。計算結果(203.00f)被轉換成int類型,并存儲在i中。
- 第13行和第14行:ch的值('D',或68)被轉換成浮點數,然后2乘以ch。為了做加法,i的值(203)被轉換為浮點類型。計算結果(339.00)被存儲在fl中。
- 第15行和第16行:演示了類型降級的示例。把ch設置為一個超出其類型范圍的值,忽略額外的位后,最終ch的值是字符S的ASCII碼。或者,更確切地說,ch的值是1107%256,即83。
- 第17行和第18行:演示了另一個類型降級的示例。把ch設置為一個浮點數,發生截斷后,ch的值是字符P的ASCII碼。
3 強制類型轉換運算符
通常,應該避免自動類型轉換,尤其是類型降級。但是如果能小心使用,類型轉換也很方便。我們前面討論的類型轉換都是自動完成的。然而,有時需要進行精確的類型轉換,或者在程序中表明類型轉換的意圖。這種情況下要用到強制類型轉換(cast),即在某個量的前面放置用圓括號括起來的類型名,該類型名即是希望轉換成的目標類型。圓括號和它括起來的類型名構成了強制類型轉換運算符(cast operator),其通用形式是:
(type)
用實際需要的類型(如,long)替換type即可。
考慮下面兩行代碼,其中mice是int類型的變量。第2行包含兩次int強制類型轉換。
mice = 1.6 + 1.7;
mice = (int)1.6 + (int)1.7
第1行使用自動類型轉換。首先,1.6和1.7相加得3.3。然后,為了匹配int類型的變量,3.3被類型轉換截斷為整數3。第2行,1.6和1.7在相加之前都被轉換成整數(1),所以把1+1的和賦給變量mice。本質上,兩種類型轉換都好不到哪里去,要考慮程序的具體情況再做取舍。
一般而言,不應該混合使用類型(因此有些語言直接不允許這樣做),但是偶爾這樣做也是有用的。C語言的原則是避免給程序員設置障礙,但是程序員必須承擔使用的風險和責任。
4 總結 C的一些運算符
下面是我們學過的一些運算符。
賦值運算符:
- = 將其右側的值賦給左側的變量算術運算符:
- + 將其左側的值與右側的值相加
- - 將其左側的值減去右側的值
- - 作為一元運算符,改變其右側值的符號
- * 將其左側的值乘以右側的值
- / 將其左側的值除以右側的值,如果兩數都是整數,計算結果將被截斷
- % 當其左側的值除以右側的值時,取其余數(只能應用于整數)
- ++ 對其右側的值加1(前綴模式),或對其左側的值加1(后綴模式)
- -- 對其右側的值減1(前綴模式),或對其左側的值減1(后綴模式)
其他運算符:
sizeof 獲得其右側運算對象的大小(以字節為單位),運算對象可以是一個被圓括號括起來的類型說明符,如sizeof(float),或者是一個具體的變量名、數組名等,
如sizeof foo(類型名) 強制類型轉換運算符將其右側的值轉換成圓括號中指定的類型,如(float)9把整數9轉換成浮點數9.0。