C的「型別提公升」

2021-06-22 05:21:05 字數 4841 閱讀 6390

寫在前面:本來因為乙個朋友問我為什麼可以給unsigned int

賦值負數,我打算寫一篇關於解釋

unsigned

多寶平台

char、

short int

或者int型

位段(無論signed

或unsigned

)以及列舉型別

,可以使用在需要

int或者

unsigned int

的表示式中。

如果int

可以完整的表示源型別的所有值,那麼該源型別的值就轉換

為int

, 否則轉換為

unsigned int

,這被稱為整形提公升。——《

c專家程式設計》

注意:「整形提公升」是先提公升到int,而不管源資料是

signed

還是unsigned

,而不是char 

提公升到int

,unsigned char

提公升到unsigned int

。就是整型提公升和float

提公升為double

的總稱。

這個問題比較關鍵,從整形提公升的概念可以看出,在

【1

】「

char、

short int

或者int型

位段(無論signed

或unsigned

)以及列舉型別

出現在可以使用int或者

unsigned int

表示式

中」的時候,就會發生整形提公升。與此類似,我們可以得到float

到double

的提公升情況。但是

ansi c

對型別提公升有了弱化。

ansi c

標準說明如下:

char c1,c2;

c1=c1+c2; 「

型別提公升

」規則要求把每個變數提公升為int

的長度,然後兩個

int值執行加法運算,多寶

然後在對運算結果進行裁剪。如果兩個

char

的加法運算

結果不會發生溢位

,那麼實際執行時只要產生char

型別的運算結果,

可以省略型別提公升

。同樣道理也適用於float

到double

的提公升。

【2

另乙個會發生隱式型別轉換的地方就是引數傳遞。

在k&r c

中,由於函式的引數也是表示式,所以也會發生型別提公升。 

在ansi c

中,如果使用了適當的函式原型,型別提公升便不會發生,否則也會發生。

在被呼叫函式內部,提公升後的引數被裁剪為原先宣告的大小。

這就是為什麼單個的printf()

格式字串

%d能適用於幾個不同型別,

short

,char

或int

,而不論實際傳遞的是上述型別的哪乙個。

函式從堆疊中取出的引數總是int

型別,並在printf

[1]或其他或其他被呼叫的函式裡按統一格式處理。

所以如果使用printf

來列印比

int長的型別,如

long long

,除非使用

long long

格式化限定符

%ld,否則無法獲取正確的值。

因為在缺少更多資訊的情況下,printf

假定他處理的資料是

int型別。

[1]即使乙個原型位於

printf()

能及的範圍內,注意它的原型以省略號結尾:

int printf(const char* format,...);

表示它是乙個接收可變引數的函式,引數的資訊(除第乙個外)均未給出,此時一般的引數提公升始終會發生。

總結:判斷是否發生型別提公升記住上述的【1】【2

】即可。

(1) printf(「%d

」,sizeof(『a

』));

//輸出4

分析:sizeof()

是乙個表示式,字元』a

』由char

型別提公升到

int,所以列印出是4(

int的大小),而不是1(

char

的大小)。

(2)char a,b;

printf ( " the size of the result of a+b :%d " ,sizeof( a+b) );  //輸出4

* (3)char a;

printf ( "%d\n" ,sizeof(a)); //輸出1

這個題要引起注意,為什麼這裡沒有發生型別提公升呢?我的理解是這樣的:

當傳遞的是字元常量的時候,

sizeof

是根據數值推斷的,而當表示式使用到

值的時候,就會進行進行型別提公升(回憶整型提公升定義,這裡可以用int

值代替)。但當引數是變數時,此時

sizeof(a)

實際等價於

sizeof(char)

,並沒有用到數

值,所以輸出1

。我們把提公升分為兩個階段——擴充套件和解釋。

要想進行型別提公升,很明顯首先需要進行位擴充套件,比如char(1

位元組)提公升到

int(

4位元組)要在高位擴充套件

3個位元組。那麼是如何擴充套件呢?答案是:

有符號數按照有符號數的擴充套件規則(高位補符號位)擴充套件,無符號數按照無符號數的擴充套件規則(高位補0

)擴充套件

我們先看乙個例子:

int main()

/* *case1:unsigned int a=0x12345678;

*output:0078  0078 *

*case2:unsigned int a=0x123456a8;

*output:0078 ffffffa8; */

分析:printf("%04x %04x\n",b,*c ); //實參為

char,

則要進行整形提公升

*c是有符號的,做有符號擴充套件,高位補符號位。在

case1

中(78

)符號位為

0,因此提公升後為:

0x00000078

,按寬度至少

4位輸出即

0078

。而在case2

中(a8

)符號位為

1,因此提公升後為:

0xffffffa8,

由於全是有效位,則忽略按

4位寬度,輸出:

ffffffa8。 b

提公升成int

,因為b

是無符號的,所以做無符號擴充套件,高位補0;

單單擴充套件完並沒有完成任務,因為這涉及到程式該如何理解這些二進位制位。這裡我們再回到最開始的「整型提公升」的定義——

如果int

可以完整的表示源型別的所有值,那麼該源型別的值就轉換

為int

, 否則轉換為

unsigned int

。所以得到結論,優先解釋為

int。我們先看乙個例子:

char a = -1;

unsigned char b = 1;

printf("%d", a > b);//輸出0

分析:這個過程是這樣的,首先對表示式a>b

進行整型提公升,a是

-1,記憶體表示為

0xff,由於a

是有符號數,所以擴充套件為

0xffffffff;b

是1,記憶體表示為

0x01

,由於b

是無符號的,所以擴充套件為

0x00000001

。接下來是解釋,編譯器都將其解釋為

int(注意,這裡同為

int,所以不發生算數轉換),所以

0xffffffff

解釋為-1

,0x00000001

解釋為1.

因為-1<1;

所以a>b

不成立,輸出0。

這個例子要和下面這個例子區別:

int a = -1;

unsigned int b = 1;

printf("%d", a > b);//輸出1

這個沒有發生型別提公升(變數已經是int

),而是算數轉換,我們後面專題再介紹。

那什麼時候解釋為unsigned int

呢?答案是有

unsigned int

參與的時候,比如下面這個例子:

char a= -1;

unsigned int b = -1;

printf("%d\n",a==b);//輸出1

其實這個應該算是算數轉換,我們可以把它看做一次整形提公升+

一次算數轉換,首先將

char

提公升為int

,int

和unsigned int

的比較和運算就要涉及到算數轉換了。

所以我們可以簡單地記住,整形提公升都解釋為int

C語言裡的型別提公升

一 型別的提公升 把char unsigned char short unsigned short轉換成int型別稱為型別提公升 promotion 1.如果short的位元組長度小於int的位元組長度 char轉換成 int unsigned char轉換成 int short轉換成 int un...

C中的型別自動提公升

同一句語句或表示式如果使用了多種型別的變數和常量 型別混用 c 會自動把它們轉換成同一種型別。以下是自動型別轉換的基本規則 1.在表示式中,char 和 short 型別的值,無論有符號還是無符號,都會自動轉換成 int 或者 unsigned int 如果 short 的大小和 int 一樣,un...

c語言中經常忽略的型別提公升

型別提公升常發生在表示式中,char,short 包括它們的signed和unsigned型別 以及列舉型別enum,在表示式中被提公升為int 如果int足夠表示轉換後的數的話,否則就是unsigned int 在含有unsigned int的表示式裡,int轉換為unsigned int來做運算...