寫在前面:本來因為乙個朋友問我為什麼可以給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來做運算...