C++帶符號類型和無符號類型


 

//摘自 C++ Primer 第5版

各種基本數據類型地大小

 char 字符類型 類型占用8位

 short 短整型 16位

 int 整型 16位

 long 長整型 32位

 long long 長整型 32位

 float 浮點類型 類型占用32位 1 符號位 8 指數位 23 尾數位

 double 浮點類型 類型占用64位 1 符號位 11 指數位 52 尾數位

 

帶符號類型和無符號類型

除去布爾型和擴展的字符型之外,其他整型可以划分為帶符號的( signed)和無符號的(unsigned)兩種。

帶符號類型可以表示正數、負數或0,無符號類型則僅能表示大於等於0的值。
類型int、 short、long 和 long long 都是帶符號的,通過在這些類型名前添加unsigned就可以得到無符號類型,

例如 unsigned long。類型unsigned int可以縮寫為unsigned。
與其他整型不同,字符型被分為了三種: char. signed char和 unsigned charo

特別需要注意的是:類型char和類型signed char並不一樣。盡管字符型有三種,但是字符的表現形式卻只有兩種:帶符號的和無符號的。類型char實際上會表現為上述兩種形式中的一種,具體是哪種由編譯器決定。
無符號類型中所有比特都用來存儲值,例如,8比特的unsigned char可以表示0至255區間內的值。
C++標准並沒有規定帶符號類型應如何表示,但是約定了在表示范圍內正值和負值的量應該平衡。因此,8比特的signed char理論上應該可以表示-127至127區間內的值,大多數現代計算機將實際的表示范圍定為-128至127。

建議:如何選擇類型
和C語言一樣,C++的設計准則之一也是盡可能地接近硬件。C++的算術類型必須滿足各種硬件特質,所以它們常常顯得繁雜而令人不知所措。事實上,大多數程序員能夠(也應該)對數據類型的使用做出限定從而簡化選擇的過程。以下是選擇類型的一些經驗准則:
  1.當明確知曉數值不可能為負時,選用無符號類型。
  2.使用int執行整數運算。在實際應用中,short常常顯得太小而long一般和 int有一樣的尺寸。如果你的數值超過了int的表示范圍,選用long long.
  3.在算術表達式中不要使用 char或bool,只有在存放字符或布爾值時才使用它們。因為類型char在一些機器上是有符號的,而在另一些機器上又是無符號的,所以如果        使用char進行運算特別容易出問題。如果你需要使用一個不大的整數,那么明確指定它的類型是signed char或者unsigned char。
  4.執行浮點數運算選用double,這是因為float通常精度不夠而且雙精度浮點數和單精度浮點數的計算代價相差無幾。事實上,對於某些機器來說,雙精度運算甚至比單精度還快。long double提供的精度在一般情況下是沒有必要的,況且它帶來的運行時消耗也不容忽視。

 

類型轉換 

unsigned char c = -1; //假設char占8比特,c的值為255
unsigned char c2 = 256; //假設char占8比特,c的值是未定義的


當我們賦給無符號類型一個超出它表示范圍的值時,結果是初始值對無符號類型表示數值總數取模后的余數。例如,8比特大小的unsigned char可以表示0至255 區間內的值,如果我們賦了一個區間以外的值,則實際的結果是該值對256取模后所得的余數。因此,把-1賦給8比特大小的unsigned char所得的結果是255。

當我們賦給帶符號類型一個超出它表示范圍的值時,結果是未定義的(undefined)。此時,程序可能繼續工作、可能崩潰,也可能生成垃圾數據。

 

含有無符號類型的表達式

盡管我們不會故意給無符號對象賦一個負值,但實際缺很常見

unsigned u = 10;
int i = -42;
std::cout << i + i << std::endl;//輸出-84
std::cout << u + i << std::endl;//如果int占32位,輸出4292967264

解釋:
  相加前首先把整數-42轉換成無符號數,把負數轉換成無符號數類似於直接給無符號數賦一個負值,
  結果等於這個負數加上無符號數的模。

當從無符號數中減去一個值時,不管這個值是不是無符號數,我們都必須確保結果不能是一個負值:

unsigned ul = 42,u2= 10;
std::cout << ul -u2 << std::endl;//正確:輸出32
std::cout << u2 - ul << std::endl;// 正確:不過,結果是取模后的值

無符號數不小於0這個事實一定要牢記,涉及一個經典的死循環錯誤。

如果你想寫一個循環

for (int i = 10; i >= 0; --i)
  std::cout << i << std::endl;//這樣寫完全沒有問題

可能你會覺得反正也不打算輸出負數,可以用無符號數來重寫這個循環。
然而,不經意的改變意味着死循環。
或者,你在循環中使用了

vector<int> nums;
nums.size()//使用了vector的這個函數,其返回值是無符號數

那么,會出現下面的場景:(關於vector的詳細用法,可參考C++中的vector

for (unsigned u = 10; u >= 0; --u)
  std::cout << u << std::endl;

for (unsigned u = nums.size(); u >= 0; --u)
  std::cout << u << std::endl;

來看看當u等於0時發生了什么,這次迭代輸出了0,然后繼續執行for語句里面的表達式,
表達式--u從u當中減去1,得到的結果-1並不滿足無符號數的要求。此時,像所有表示范圍之外的其他數字一樣,
-1被自動地轉換成了一個合法地無符號數。假設int類型占32位,則當u等於0時, --u地結果將會是4294967295。

一種解決的辦法是,用while語句來替代for語句,因為前者讓我們能夠在輸出變量之前(而非之后)先減去1:

unsigned u = 11;//確定要輸出的最大數,從比它大1的數開始
while (u > 0) {
  --u;    //先減1,這樣最后一次迭代就會輸出0
  std::cout << u << std::endl;
}

改寫后的循環先執行對循環控制變量減1的操作,這樣最后一次迭代時,進入循環的u值為1。此時將其減1,則這次迭代輸出的數就是0;下一次再檢驗循環條件時,u的值等於0而無法再進入循環。因為我們要先做減1的操作,所以初始化u的值應該比要輸出的最大值大1。這里,u初始化為11,輸出的最大數是10。

提示:切勿混用帶符號類型和無符號類型

如果表達式里既有帶符號類型又有無符號類型,當帶符號類型取值為負時會出現異常結果,這是因為帶符號數會自動地轉換成無符號數。例如,在一個形如 a*b的式子中,如果a =-1,b=1,而且a和b都是int,則表達式的值顯然為-1。然而,如果a是int,而b是unsigned,則結果須視在當前機器上int所占位數而定。在我們的環境里,結果是4294967295。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM