在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关键字。