现在的位置: 首页 > 综合 > 正文

小议C#语言对整数的越界检查

2012年02月07日 ⁄ 综合 ⁄ 共 1700字 ⁄ 字号 评论关闭

在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#中,常量越界会引发编译错误(即使是用了强制转换),如下:

image

 

 

正如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选项中,选“高级”,选中“检查运算溢出”。

image

 

这样的话工程内所有代码就好像在checked块中运行了。如果某处不需要检查溢出,则用unchecked关键字。

 

:D

抱歉!评论已关闭.