1. 程式人生 > >C的隱式類型轉換

C的隱式類型轉換

賦值運算符 有一個 ffffff 表達 計時 可能 不同類 類型 有時

這篇文章僅僅針對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類型大的數據類型。其實在進行運算時,是以表達式中最長類型為準的,將其他類型轉換成該類型,具體的規則如下:

  1. 比int類型小的類型(char, signed char, unsigned char, short, unsigned short),先經過整型提升,提升為int類型,然後int類型再根據表達式中最長類型轉換為該類型。
  2. int、long(4字節) -->> unsinged int、unsinged long(4字節) -->> long long (8字節)-->> unsigned long long (8字節)<<-- double(8字節) <<-- float(4字節) (32位系統中)
  3. float -->> int(32位系統中)
  4. 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的隱式類型轉換