C/C++中關於char是有符號還是無符號及其溢出問題

1、char的有無符號類型

char 分爲有符號性(signed)和無符號型(unsigned)兩種:

Ø        若是signed型,就意味着取值範圍爲[-128,127];

Ø        若是unsigned型,就意味着取值範圍爲[0,255];

C語言中我們通常直接用類型char,但是它究竟是被當做signed型還是unsigned型,由編譯器決定。

C語言允許我們在char前面加上關鍵字signed或者unsigned,這樣,無論在編譯器中被當做signed還是unsigned,都會按照前面加的這個關鍵字來決定。

例如:假設我們現在使用的編譯器,把char當做signed來看到,則

char c1;

signed c2;

unsigned c3;

則c1和c2的取值範圍都是[-128,127],而c3的取值範圍則是[0,255]。

所謂取值範圍,是指其值在這個範圍之內時,會被正確處理,超出這個範圍就會發生溢出。但在這個範圍之內,並不意味着它就是可打印字符。這點不要混淆。

 

2、溢出

1)有符號

c1和c2一樣,我們以c1爲例來說明。先看向上超過上界的情況:

十進制

十六進制

二進制

126

0000007e

0…0 0111 1110

126

127

0000007f

0…0 0111 1111

127

128

ffffff80

1…1 1000 0000

-128

129

ffffff81

1…1 1000 0001

-127

130

ffffff82

1…1 1000 0010

-126

注:0…0表示24個0,1…1表示24個1

 

十進制

十六進制

二進制

-127

ffffff81

1…1 1000 0001

-127

-128

ffffff80

1…1 1000 0000

-128

-129

0000007f

0…0 0111 1111

127

-130

0000007e

0…0 0111 1110

126

-131

0000007d

0…0 0111 1101

125

-256

00000000

0…0 0000 0000

0

-257

ffffffff

1…1 1111 1111

-1

 

從這兩個表中可以看到,不管是否超過上下屆也,不管是負數還是正數,每增加(減少)一個單位,就直接在二進制表示的最後一位上加(減)1。

相加或相減時,都可以先不看前24位:如果倒數第8位變爲1,則前面24位全部設置爲1,該數被解釋爲負數;如果倒數第8位變爲0,則前面24位全部設置爲0,該數被解釋爲正數。下面我們挑幾個例子詳細說明:

c1=128;

這時,會在127的最後8位上加1,原來的8位是0111 1111,加1後,變爲1000 0000,於是把前面24個位也全部變爲1,且該數被表示爲負數。其值爲-1*2^7+0=-128.

c1=129;

128的最後8位是1000 0000,加上後爲1000 0001,前面24位保持1不變。該數被解釋爲負數,其值爲-1*2^7+1=-127.

c1=-129;

-128的最後8位是1000 0000,減去1變爲0111 1111,前面24位全部置位0,且該數被解釋爲正數,其值爲2^0+2^1+2^2+2^3+2^4+2^5+2^6=2^7-1=127

c1=-130;

-127的最後8位是0111 1111,減去1變爲0111 1110,前面24位全部保持0,且該數被解釋爲正數,其值爲2^1+2^2+2^3+2^4+2^5+2^61=126

c1=-257;

由前面可以類推, -256的最後8位編碼是0000 0000,減去1後變爲1111 1111,前面24爲全部置爲1,該數被解釋爲負數,值爲-1*2^7+2^0+2^1+2^2+2^3+2^4+2^5+2^6=-1。

注意:00000000減去1,也就是減去0000 0001,單看這8位的話,前面的數較小,我們可以想象在前面那個數的前面再加一個1,變成0001 0000 0000,後面那個數前面加0,變成0000 0000 0000,相減後,我們只看最後8位即可。

 

2)無符號

我們以c3爲例進行說明。

十進制

十六進制

二進制

254

000000fe

0…0 1111 1110

254

255

000000ff

0…0 1111 1111

255

256

00000000

0…0 0000 0000

0

257

00000001

0…0 0000 0001

1

258

00000002

0…0 0000 0010

2

 

十進制

十六進制

二進制

-2

0000007e

0…0 1111 1110

254

-1

0000007f

0…0 1111 1111

255

0

ffffff80

0…0 0000 0000

0

1

ffffff81

0…0 0000 0001

1

2

ffffff82

0…0 0000 0010

2

 

其實和有符號型基本相同,唯一的差別就是,前面24位一直都置位0,且該數永遠被解釋爲整數。下面舉例說明:

c3=256;

因爲255的最後8位是1111 1111,加1後變爲0000 0000,所以其值爲0;

c3=-1;

因爲0的最後8位是0000 0000,減1後變爲1111 1111,所以其值爲255;

 

0000 0000減1,相當於減去0000 0001,雖然0的全碼是0…0 0000 0000,似乎往上也沒法借位,但是我們仍可將0000 0000前面一位加上1,看做0001 0000 0000減去0000 0000 0001,最後得到0000 1111 1111,我們直接去最後8爲即可。

 

歸納:不管是有符號還是無符號,在前面的數小於後面的數時,都可假設在前面的數的上一位加1,而後面的數前面加0,從而進行相減;

另外,相加時,如果最高位,即倒數第8位變爲0,只需直接將其變爲0即可,其它位不變,不需要進位。

對於無符號型,前24位永遠爲0,對於有符號型,前24位永遠和倒數第8位一樣。


那這兩者的在實際使用過程種有什麼區別呢?主要是符號位,但是在普通的賦值,讀寫文件和網絡字節流都沒什麼區別,反正就是一個字節,不管最高位是什麼,最終的讀取結果都一樣,只是你怎麼理解最高位而已,在屏幕上面的顯示可能不一樣。但是我們卻發現在表示byte時,都用unsigned char,這是爲什麼呢?首先我們通常意義上理解,byte沒有什麼符號位之說,更重要的是如果將byte的值賦給int,long等數據類型時,系統會做一些額外的工作。如果是char,那麼系統認爲最高位是符號位,而int可能是16或者32位,那麼會對最高位進行擴展(注意,賦給unsigned int也會擴展)而如果是unsigned char,那麼不會擴展。這就是二者的最大區別。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章