Elz*_*lzo 7 c# integer-overflow
试着研究C#如何对溢出做出反应,我写了这个简单的代码:
static uint diff(int a, int b)
{
return (uint)(b - a);
}
static void Main(string[] args)
{
Console.Out.WriteLine(int.MaxValue);
uint l = diff(int.MinValue, int.MaxValue);
Console.Out.WriteLine(l);
Console.In.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)
我得到这个输出:
2147483647
4294967295
Run Code Online (Sandbox Code Playgroud)
我觉得它运行得很好,因为int减法diff应该给出大于的结果int.MaxValue.
但是,如果我写这个,这似乎等同于上面的代码:
uint l = (uint)(int.MaxValue - int.MinValue);
Run Code Online (Sandbox Code Playgroud)
C#甚至不会编译,因为代码可能会溢出.
是什么让第一个代码运行没有溢出,而编译器甚至不编译第二行?
Rac*_*lan 12
使用常量值时:
uint l = (uint)(int.MaxValue - int.MinValue);
Run Code Online (Sandbox Code Playgroud)
编译器确切地知道你要做什么,因为它知道值,并且它看到减法的结果不适合a int,所以它给你错误.
使用变量时:
return (uint)(b - a);
Run Code Online (Sandbox Code Playgroud)
编译器在编译时不知道变量的值是什么,所以它不会抱怨.
请注意,溢出位于您现在删除的答案中int,而不是uint您所说的.您可能认为从小值中减去了大值,但事实并非如此.该int.MinValue实际上是负的(-2147483648),并减去这意味着你实际上是将它添加(2147483647 - (-2147483648)),所以结果(4294967295)无法适从int,但它可以适应uint.例如,这将编译并给出正确的结果4294967295:
uint x = (uint)((long)int.MaxValue - int.MinValue);
Run Code Online (Sandbox Code Playgroud)
因为现在你告诉编译器存储减法的结果long而不是,int这将起作用.现在打印x到控制台并注意到减法的结果是4294967295,而不是-1,因为你在答案中说明了.如果它像你说的那样是-1,那么下面的代码应该编译,但它不会因为4294967295溢出int:
int x = int.MaxValue - int.MinValue;
Run Code Online (Sandbox Code Playgroud)
编辑有更多的人正在努力理解结果,所以这里有更多的解释,我希望会有所帮助:
首先,我们都知道这int.MaxValue是2147483647并且int.MinValue是-2147483648.我希望我们都同意这个简单的数学,而不必在程序中证明它:
2147483647 - (-2147483648) = 4294967295
Run Code Online (Sandbox Code Playgroud)
所以我们都应该同意数学结果是4294967295,而不是-1.如果有人不同意,请回到学校.
那么为什么程序中的结果有时为-1会让很多人感到困惑呢?
好的,我们都同意发生溢出,所以这不是问题.有些人不明白溢出的发生在哪里.在施法时没有发生uint.当然-1会溢出uint,但程序int在转换之前溢出了步骤uint.发生溢出时,程序行为会根据执行上下文(已选中或未选中)而有所不同.在已检查的上下文中,OverflowException抛出a,之后uint不执行任何操作,因此不执行转换.在未经检查的上下文中,结果的最高有效位被丢弃并继续执行,因此执行转换,uint然后发生另一次溢出.这是一篇关于整数溢出如何表现的MSDN文章.
那么让我们看看我们如何获得-1:
首先,在C#中,当你减去两个整数时,结果是一个整数.现在,如果结果不能适合整数,则会发生溢出.棘手的部分是在未经检查的上下文中,结果的最高有效位被丢弃,就像我上面提到的那样.在问题的场景中,结果为-1.以下是一些我希望能够明确表达的例子:
Console.WriteLine(unchecked(int.MaxValue)); //Result 2147483647
Console.WriteLine(unchecked(int.MinValue)); //Result -2147483648
Console.WriteLine(unchecked(int.MaxValue-int.MinValue)); //Result -1 overflow
Console.WriteLine(unchecked(2147483647-(-2147483648))); //Same as above
Console.WriteLine(unchecked(int.MaxValue+int.MinValue)); //Result -1 no overflow
Console.WriteLine(unchecked(2147483647+(-2147483648))); //Same as above
Console.WriteLine(unchecked(int.MaxValue+1)); //Result -2147483648 overflow
Console.WriteLine(unchecked(2147483647+1)); //Same as above
Console.WriteLine(unchecked(int.MaxValue-int.MaxValue)); //Result 0
Console.WriteLine(unchecked(2147483647-2147483647)); //Same as above
Console.WriteLine(unchecked(int.MaxValue+int.MaxValue)); //Result -2 overflow
Console.WriteLine(unchecked(2147483647+2147483647)); //Same as above
Run Code Online (Sandbox Code Playgroud)
这些例子的结果应该是清楚的.我没有在这里做任何演员以避免关于溢出发生在哪里的争论,所以很明显它正在发生int.每次发生溢出时,就好像第一个int被赋值int.MinValue为-2147483648,然后第二个int被加/减.
如果你将第一个数字转换为long,那么结果将是long.现在不会发生溢出,您将获得与数学中相同的结果:
Console.WriteLine((long)int.MaxValue); //Result 2147483647
Console.WriteLine((long)int.MinValue); //Result -2147483648
Console.WriteLine((long)int.MaxValue-int.MinValue); //Result 4294967295
Console.WriteLine((long)2147483647-(-2147483648)); //Same as above
Console.WriteLine((long)int.MaxValue+int.MinValue); //Result -1
Console.WriteLine((long)2147483647+(-2147483648)); //Same as above
Console.WriteLine((long)int.MaxValue+1); //Result 2147483648
Console.WriteLine((long)2147483647+1); //Same as above
Console.WriteLine((long)int.MaxValue-int.MaxValue); //Result 0
Console.WriteLine((long)2147483647-2147483647); //Same as above
Console.WriteLine((long)int.MaxValue+int.MaxValue); //Result 4294967294
Console.WriteLine((long)2147483647+2147483647); //Same as above
Run Code Online (Sandbox Code Playgroud)
这里是没有使用任何加法/减法的证据.简单地在上面输出一个值int.MaxValue会导致unchecked转换为溢出int.MinValue.任何大于的值int.MaxValue + 1都将添加到int.MinValue:
Console.WriteLine(unchecked((int)2147483647)); //Result 2147483647
Console.WriteLine(unchecked((int)2147483648)); //Result -2147483648 overflow
Console.WriteLine(unchecked((int)2147483649)); //Result -2147483647 overflow
Console.WriteLine(unchecked((int)2147483649)); //Result -2147483647 overflow
Console.WriteLine(unchecked((int)2147483650)); //Result -2147483646 overflow
Console.WriteLine(unchecked((int)2147483651)); //Result -2147483645 overflow
Run Code Online (Sandbox Code Playgroud)
当你溢出int下面的值时,恰好相反int.MinValue:
Console.WriteLine(unchecked((int)-2147483648)); //Result -2147483648
Console.WriteLine(unchecked((int)-2147483649)); //Result 2147483647 overflow
Console.WriteLine(unchecked((int)-2147483650)); //Result 2147483646 overflow
Console.WriteLine(unchecked((int)-2147483651)); //Result 2147483645 overflow
Run Code Online (Sandbox Code Playgroud)
这使得int工作就像一个无限旋转的旋转器.两端彼此相邻,所以当你到达一端时,你翻到另一端并继续1 2 3 1 2 3 1 2 3.