C的隱式類型轉換
這篇文章僅僅針對C語言存在的隱式類型轉換做一些分析,關於C++的這方面研究,有時間我再另外寫一篇文章。
關於隱式類型轉換,是指發生在沒有明確說明的情況下(C語言風格的強制類型轉換就是屬於我們程序員有明確說明的),編譯器自動幫我們執行的類型轉換。
通常同類型的數據進行運算、比較和賦值的時候我們是不需要擔心的,這裏我只是說明不同類型的數據進行運算、比較和賦值時,且我們程序員沒有指定類型轉換時,編譯器是如何幫我們進行處理類型之間的轉換的,只有知道這個過程,才能讓我們知道程序設計時應該註意和避免的地方。
一、比int類型小的隱式類型轉換:整型提升
想要知道隱式類型轉換,我們有必要了解一下整型提升(Integer Promotion),這也是屬於隱式類型轉換的一種方式。
整型提升是C程序設計語言中的一項規定:在表達式計算時(包括比較運算和算術運算等),比int類型小的類型(char, signed char, unsigned char, short, unsigned short等)首先要提升為int類型,然後再執行表達式的運算。
具體的我們從下面的程序進行分析吧
#include <stdio.h> int main() { signed char a = -1; unsigned char b = a; if(a == b) printf("a==b"); else if(a<b) printf("a<b"); else printf("a>b"); return 0; }
執行結果我想很多人都可以想到是:a<b
但是編譯器是怎麽處理這個過程的呢??答案就是整型提升。對比int類型小的類型的每一次表達式計算都伴隨整型提升,所以在對char型的變量進行比較運算時(a > b,a < b,a==b這些比較運算),編譯器首先會對char型變量進行Integer Promotion,也就是將char型變量提升成int類型的,然後再進行比較。
至於提升的方法,是根據原始類型進行位擴展(如果原始類型為unsigned char,進行零擴展,如果原始類型為signed char,進行符號位擴展)到32位。拿上文代碼作為例子,a是signed char型的,a=-1在內存中的位儲存形式是0xff,把a賦值給b,隨意b在內存中的位儲存形式也是0xff;然後就是a與b進行比較運算了,編譯器會把a,b都提升到int類型,那麽原來a在內存中的位形式是0xff,提升為int類型後會變成0xffffffff(符號位擴展),原來b在內存中的位形式是0xff,提示為int類型後會變成0x000000ff(零擴展),可以看出,此時a是小於b的。等等?a < b?對的,沒錯,a是小於b的,因為Integer Promotion之後,a,b都暫時(只是暫時,僅僅只是在執行運算時提升了)提升為int類型了,也就是signed int類型。
其實char a += 1;
這個表達式就已經隱含了Integer Promotion。
為什麽編譯器要進行Integer Promotion?
學過微機原理或者學習過匯編的同學可能會知道,在我們的CPU中有一個算術邏輯單元(Arithmetic&logical Unit),簡稱ALU,主要功能是進行二位元的算術運算,如加減乘(不包括整數除法)和寄存器中的值之間的邏輯運算。那麽,我們的C/C++程序進行的運算最終也是要在ALU中進行的,以32位CPU為例,寄存器都是32位的(剛好是一個int類型所占用的位數),想要把char類型的變量送進ALU中運算,那必然是需要把char類型變成32位,然後通過32位寄存器送入ALU,那麽這個時候Integer Promotion的意義就出來了,如果不這樣進行提升,ALU就無法對char類型的變量進行運算了。
二、比int類型大的隱式類型轉換
上面說了整型提升,只是針對表達式中沒有比int類型大的數據類型。其實在進行運算時,是以表達式中最長類型為準的,將其他類型轉換成該類型,具體的規則如下:
- 比int類型小的類型(char, signed char, unsigned char, short, unsigned short),先經過整型提升,提升為int類型,然後int類型再根據表達式中最長類型轉換為該類型。
- int、long(4字節) -->> unsinged int、unsinged long(4字節) -->> long long (8字節)-->> unsigned long long (8字節)<<-- double(8字節) <<-- float(4字節) (32位系統中)
- float -->> int(32位系統中)
- double -->> long long
下面還是來看看一段程序,與第一段程序做個對比
#include <stdio.h>
int main()
{
signed int a = -1;
unsigned int b = a;
if(a == b)
printf("a==b");
else if(a<b)
printf("a<b");
else
printf("a>b");
return 0;
}
執行結果如下:
a==b
對比下第一段程序,我們很快就能發現區別!
三、賦值時的隱式類型轉換
前面提到的都是在進行計算時存在的隱式類型轉換,還要特別說明的就是在進行賦值操作時,賦值運算符右邊的數據類型必須轉換成賦值號左邊的類型,若右邊的數據類型的長度大於左邊,則要進行截斷或舍入操作。
下面用一實例說明:
char ch;
int i,result;
float f;
double d;
result=ch/i+(f*d-i);
(1)首先計算 ch/i,ch → int型,ch/i → int型。
(2)接著計算 fd-i,由於最長型為double型,故f→double型,i→double型,fd-i→double型。
(3)(ch/i) 和(fd-i)進行加運算,由於fd-i為double型,故ch/i→double型,ch/i+(f*d-i)→double型。
(4)由於result為int型,故ch/i+(f*d-i)→double→int,即進行截斷與舍入,最後取值為整型。
C的隱式類型轉換