在C/C++中,整數變數的膚質是從來不進行越界檢查的,於是你可以給一個int變數成功賦予比INT_MAX(C/C++中定義的int的最大值)都大的數字,或者給不帶正負號的整數賦予一個負數值,結果就是值會被暴力塞到對應變數的空間內,因此二進位中多餘的高位可能會被剪掉。
事實上C語言標準並沒有強制這樣做,但是C語言的具體執行通常都會這樣的。
可以參考C語言標準4.7 Integral conversions
參考連結:http://www.slac.stanford.edu/BFROOT/www/Computing/Environment/Standards/C++/cd2/conv.html
範例程式碼:
//======= C/C++代碼 =======
cout << "INT_MAX: " << INT_MAX << endl;
//4294967297 (十六進位:0x100000001(33位))
int i1 = 0x100000001;
int i2 = 4294967297;
cout << i1 << endl;
cout << i2 << endl;
cout << "UINT_MAX: " << UINT_MAX << endl;
//-1的反碼值為:0xFFFFFFFF
unsigned int c = -1;
cout << c <<endl;
可以看到,第一段,我們將33位的一個數字賦給只能存放32位的int變數,於是最高位1被剪掉了,只有下中間一大堆0和最後一個1,所以最後變數輸出為1。第二段我們把-1賦值給一個無符號int,由於-1的反碼所有位都是1,所以最後輸出無符號int的最大值。
結果:
INT_MAX: 2147483647
1
1
UINT_MAX: 4294967295
4294967295
當然在C#中,常量越界會引發編譯錯誤(即使是用了強制轉換),如下:
正如Visual Studio的錯誤提示所說的,我們可以使用C#中的unchecked關鍵字來取消這樣的限制,如下代碼:
int i = unchecked((int)0x100000001);
uint ui = unchecked((uint)-1);
Console.WriteLine(i);
Console.WriteLine(ui);
這樣會成功輸出:
1
4294967295
上面講的是常量聲明中的越界檢查。當然,變數之間的強制轉換預設不會進行越界檢查的,所以還可以有另一種方法來實現上面代碼的功能,就是用變數之間的強制轉換:
//不直接使用常量
long i_long = 0x100000001;
int i_signed = -1;
int i = (int)i_long;
uint ui = (uint)i_signed;
Console.WriteLine(i);
Console.WriteLine(ui);
輸出會和上面一樣。
當然上面這些代碼的溢出都會悄悄發生不會被覺察,在某些時候可能會誘發Bug的出現,所以可以使用checked關鍵字來捕獲溢出錯誤(通過在catch中捕獲OverflowException),如下代碼:
long i_long = 0x100000001;
int i_signed = -1;
checked
{
try
{
int i = (int)i_long;
uint ui = (uint)i_signed;
Console.WriteLine(i);
Console.WriteLine(ui);
}
catch (OverflowException)
{
Console.WriteLine("溢出錯誤");
}
}
上面代碼因為在checked塊內,所以會檢查運算(或者轉換)中的溢出情況,當有溢出發生後,OverflowException異常會被拋出。
如果你想在工程範圍內預設進行變數強制轉換的溢出檢查,可以在Visual Studio工程屬性中的Build選項中,選“進階”,選中“檢查運算溢出”。
這樣的話工程內所有代碼就好像在checked塊中運行了。如果某處不需要檢查溢出,則用unchecked關鍵字。