当你使用常量值时:
uint l = (uint)(int.MaxValue - int.MinValue);
编译器确切地知道您要执行的操作,因为它知道值,并且它发现减法的结果不适合 int,因此它会给您错误。
当你使用变量时:
return (uint)(b - a);
编译器在编译时不知道变量的值是什么,所以它不会抱怨。
请注意,溢出在int,而不是uint,正如您在现在已删除的答案中所述。您可能认为您正在从小价值中减去大价值,但事实并非如此。 int.MinValue 实际上是负数 (-2147483648),减去它意味着您实际上是在添加它 (2147483647 - (-2147483648)),所以结果 (4294967295) 不适合 int,但它可以适合 @987654330 @。例如,这将编译并给出正确的结果 4294967295:
uint x = (uint)((long)int.MaxValue - int.MinValue);
因为现在您告诉编译器将减法的结果存储在 long 而不是 int 中,这样就可以了。现在将x 打印到控制台并注意减法的结果是4294967295,而不是您在答案中所说的-1。如果像你说的那样是-1,那么下面的代码应该可以编译,但它不会因为 4294967295 溢出int:
int x = int.MaxValue - int.MinValue;
编辑有更多的人在努力理解结果,所以这里有更多的解释,我希望能有所帮助:
首先,我们都知道int.MaxValue是2147483647,int.MinValue是-2147483648。我希望我们也都同意这个简单的数学,而不必在程序中证明它:
2147483647 - (-2147483648) = 4294967295
所以我们都应该同意数学结果是 4294967295,而不是 -1。如果有人不同意,请回到学校。
那么为什么程序中的结果有时是-1,这让很多人感到困惑?
好的,我们都同意发生溢出,所以这不是问题。有些人不明白溢出发生在哪里。投射到uint 时不会发生这种情况。当然-1会溢出uint,但是程序在转换为uint之前的步骤中溢出了int。发生溢出时,程序行为会根据执行上下文(选中或未选中)而有所不同。在经过检查的上下文中,会抛出 OverflowException,之后不会执行任何操作,因此不会执行对 uint 的强制转换。在未经检查的上下文中,结果的最高有效位被丢弃并继续执行,因此然后执行转换为uint 并发生另一个溢出。这里是an MSDN article about how the integer overflows behave。
那么让我们看看我们是如何得到 -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
这些例子的结果应该是清楚的。我在这里没有做任何转换以避免关于溢出发生在哪里的争论,所以很明显它发生在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
这是不使用任何加法/减法的证据。简单地在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
当您使用低于int.MinValue 的值溢出int 时,会发生完全相反的情况:
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
这使得int 像一个无限旋转的微调器一样工作。两端粘在一起,所以当你到达一端时,你翻转到另一端并继续 1 2 3 1 2 3 1 2 3。