usr*_*usr 24 c# language-design
该表达式int.Minvalue / -1根据C#规范导致实现定义的行为:
7.8.2分部运营商
如果左操作数是最小的可表示的int或long值而右操作数是-1,则发生溢出.在已检查的上下文中,这会导致抛出System.ArithmeticException(或其子类).在未经检查的上下文中,它是实现定义的,是否抛出System.ArithmeticException(或其子类)或溢出未报告,结果值是左操作数的值.
测试程序:
var x = int.MinValue;
var y = -1;
Console.WriteLine(unchecked(x / y));
Run Code Online (Sandbox Code Playgroud)
这会引发OverflowException.NET 4.5 32位,但它没有.
为什么规范会保留结果实现定义?这是反对这样做的情况:
idiv在这种情况下,x86 指令总是会导致异常.同样有趣的是,如果x / y是一个编译时常量我们确实得到unchecked(int.MinValue / -1) == int.MinValue:
Console.WriteLine(unchecked(int.MinValue / -1)); //-2147483648
Run Code Online (Sandbox Code Playgroud)
这意味着,x / y可以有根据使用的语法形式在不同的行为(而不是只依赖于的值x和y).这是规范允许的,但它似乎是一个不明智的选择.为什么C#这样设计?
一个类似的问题指出,在说明书这一确切行为被规定,但它并没有(足够的)回答为什么语言是这样设计的.不讨论替代选择.
Han*_*ant 10
这是C#语言规范的更大兄弟Ecma-335(公共语言基础结构规范)的副作用.第III节第3.31章描述了DIV操作码的作用.C#规范通常必须遵循的规范,这是不可避免的.它指定它可以抛出但不要求它.
否则,对真实处理器的作用进行实际评估.每个人都使用的那个是奇怪的.英特尔处理器对溢出行为过于古怪,它们是在20世纪70年代设计的,假设每个人都会使用INTO指令.没有人,另一天的故事.它不会忽略IDIV上的溢出然后提升#DE陷阱,不能忽略那个响亮的爆炸声.
在处理器行为不一致的基础上,在一个粗略的运行时规范之上编写语言规范非常困难.很少有C#团队可以做到这一点,但转发不精确的语言.他们已经超越了规范,记录了OverflowException而不是ArithmeticException.很调皮.他们看了一眼.
偷看了这种做法.这不太可能是一个问题,抖动决定是否内联.并且非内联版本抛出,期望内联版本也是如此.没有人失望过.
C#的主要设计目标据称是"最小惊喜法则".根据该指南,编译器不应该试图猜测程序员的意图,而应该向程序员发出信号,要求正确指定意图需要额外的指导.这适用于感兴趣的情况,因为在二进制补码算法的限制内,操作会产生非常令人惊讶的结果:Int32.MinValue/-1计算为Int32.MinValue.发生了溢出,并且需要一个0的不可用的第33位来正确表示正确的值 Int32.MaxValue + 1.
正如预期的那样,并在您的引用中注明,在已检查的上下文中,会引发异常以提醒程序员未能正确指定意图.在未经检查的上下文中,允许实现在检查的上下文中行为,或允许溢出并返回令人惊讶的结果.有一些上下文,比如bit-twiddling,使用signed int是很方便的,但是溢出行为实际上是预期和期望的.通过检查实现说明,程序员可以确定此行为是否实际上符合预期.