1. 程式人生 > >從C++ int型別的變數範圍談起

從C++ int型別的變數範圍談起

     C/C++中,整型資料是用int來進行定義的,整型的範圍就是指int型別所能表示的整數的範圍。在32位或64位機器中,int佔4個位元組,即32位。        int能表示的最大正整數為:0111 1111 1111 1111 1111 1111 1111 1111  (最高位表示符號位,正數符號位為0)對應的10進位制數為2^31-1=2147483647,對應的十六進位制表示為:0x7FFFFFFF。        int能表示的最小負整數為:1000 0000 0000 0000 0000 0000 0000 0000  (最高位表示符號位,負數符號位為1),負數在計算機中以補碼的形式存在,所以對應的原碼(補碼的補碼)也是1000 0000 0000 0000 0000 0000 0000 0000,對應的十六進位制表示為0x80000000,而C/C++規定該值為-2^31=-2147483648。        所以最終,int型別(整數型別)的範圍為-2^31 ~ 2^31-1,即-2147483648~2147483647,十六進位制表示:0x80000000~0x7FFFFFFF。

從學習C語言開始,int型別所佔位元組數,以及數值範圍就是一個揮之不去的問題。一開始會死記硬背一個char 1個位元組,一個位元組8個bit。64位機器上面一個int 4個位元組,32位機器上面不一樣。那時候並不知道編譯器也分很多種,每一種實現的細節不一樣,也不知道各家編譯器是遵循C++標準委員會的標準。後來學會像編譯器求證,以其輸出為準,也學會向標準求證。

在一篇部落格上面看到用numeric_limits 類可以方便地瞭解各個型別的取值範圍以及佔用記憶體,於是滿心歡喜地將程式碼執行,發現有些問題:

#include<iostream> 
#include <limits> 
using namespace std; int main() { 
cout << "type: \t\t" << "------------------size-----------------------"<< endl; 
cout << "bool: \t\t" << "Bytes:" << sizeof(bool); 
cout << "\tMaxValue:" << (numeric_limits<bool>::max)(); 
cout << "\t\tMinValue:" << (numeric_limits<bool>::min)() << endl; 
cout << "char: \t\t" << "Bytes:" << sizeof(char); 
cout << "\tMaxValue:" << (numeric_limits<char>::max)(); 
cout << "\t\tMinValue:" << (numeric_limits<char>::min)() << endl; 
cout << "signed char: \t" << "Bytes:" << sizeof(signed char); 
cout << "\tMaxValue:" << (numeric_limits<signed char>::max)(); 
cout << "\t\tMinValue:" << (numeric_limits<signed char>::min)() << endl; 
cout << "unsigned char: \t" << "Bytes:" << sizeof(unsigned char); 
cout << "\tMaxValue:" << (numeric_limits<unsigned char>::max)(); 
cout << "\t\tMinValue:" << (numeric_limits<unsigned char>::min)() << endl; 
cout << "wchar_t: \t" << "Bytes:" << sizeof(wchar_t); 
cout << "\tMaxValue:" << (numeric_limits<wchar_t>::max)(); 
cout << "\t\tMinValue:" << (numeric_limits<wchar_t>::min)() << endl; 
cout << "short: \t\t" << "Bytes:" << sizeof(short); 
cout << "\tMaxValue:" << (numeric_limits<short>::max)(); 
cout << "\t\tMinValue:" << (numeric_limits<short>::min)() << endl;
 cout << "int: \t\t" << "Bytes:" << sizeof(int);
 cout << "\tMaxValue:" << (numeric_limits<int>::max)(); 
cout << "\tMinValue:" << (numeric_limits<int>::min)() << endl; 
cout << "unsigned: \t" << "Bytes:" << sizeof(unsigned); 
cout << "\tMaxValue:" << (numeric_limits<unsigned>::max)(); 
cout << "\tMinValue:" << (numeric_limits<unsigned>::min)() << endl; 
cout << "long: \t\t" << "Bytes:" << sizeof(long); 
cout << "\tMaxValue:" << (numeric_limits<long>::max)(); 
cout << "\tMinValue:" << (numeric_limits<long>::min)() << endl;
cout << "type: \t\t" << "************size**************"<< endl; return 0; }

執行結果:

常用的int,long等型別的取值範圍確實如願顯示。但是,在關於 char的判斷中,有顯示的問題。從使用的角度而言,其實char用於表示字元,判斷大小並不合理。當然,這只是一個“完形填空”式的推導。既然顯示在螢幕上的輸出有問題,本著鑽研精神,要弄清楚為什麼。

首先是假設:

1. cout無法處理這個numeric_limit<char>::max()函式

2.numeric_limit<char>::max()的返回值並不是可顯示字元。

為了驗證第一點,我找到C++標準委員會的網站,找到C++14的草稿標準:

從這裡看出,返回的是模板型別 T的建構函式。似乎排除了第一種可能。但標準太不具體,我在Clion上面用debug模式直接找到庫檔案來確認。值得一提的是編譯器版本:gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.2)。

第一步:

再往下一層:

在limits的實現裡面,都是再用巨集定義的位操作來實現最大和最小值的獲取。先看__glibcxx_digits巨集定義。這裡面的邏輯很簡單,佔用位元組數乘以__CHAR_BIT__減去符號位(1位)。在char的例子裡面,得出來的位數是7。從這裡不妨猜測,編譯器對一個位元組大小的定義很有可能是基於__CHAR_BIT__的。在一篇博文看過對於古老的機器,char型別只有7bit。按照這個猜測,在描述內建int型大小的時候,就可以說一個int等於4個char。這個猜測挺有意思,不過有待考究。

 進一步分析,__glicxx_min巨集定義,對於傳入的型別_Tp,如果有符號,就執行(_Tp)1 << _glibcxx_digits(_Tp), 沒有就是(_Tp)0.

對於char型,它居然被是認為有符號的! 所以最小值的計算變成 (char)1 << 7,二進位制表示10000000, -128.

一個被賦值為-128的char型別字元,其實現的動機難以理解,不過從某程度上說明標準與實現之間的距離,對於一個函式的實現,尤其是不常用的功能,最好還是自己進行確認。