为什么无符号整数溢出定义的行为,但有符号整数溢出不是?

无符号整数溢出由C和C ++标准定义。 例如, C99标准 (第§6.2.5/9 )说

涉及无符号操作数的计算永远不会溢出,因为无法用结果无符号整数types表示的结果被减less的模数大于可由结果types表示的最大值的数。

但是,两个标准都指出有符号整数溢出是未定义的行为。 再次,从C99标准( §3.4.3/1

未定义行为的一个例子是整数溢出行为

有没有一个历史的(或更好的!)这种差异的技术原因?

历史的原因是,大多数C实现(编译器)只是使用任何溢出行为,最容易实现与它使用的整数表示。 C实现通常使用与CPU使用相同的表示forms – 所以CPU使用的整型表示forms出现溢出行为。

在实践中,只有符号值的表示可能根据实现而有所不同:补码,二进制补码,符号幅度。 对于无符号types,标准没有理由允许变化,因为只有一个明显的二进制表示(标准只允许二进制表示)。

相关报价:

C99 6.2.6.1:3

存储在无符号位字段和unsigned chartypes对象中的值应该用纯二进制表示法表示。

C99 6.2.6.2:2

如果符号位是1,则应该用下列方法之一修改该值:

– 符号位0的相应值被否定( 符号和大小 );

– 符号位的值为 – (2 N )( 二进制补码 );

– 符号位的值为 – (2 N – 1)( 补码 )。


目前,所有的处理器都使用二进制补码表示,但是有符号的算术溢出仍然没有定义,编译器制造商希望它保持未定义,因为它们使用这个不确定性来帮助优化。 例如,参见Ian Lance Taylor撰写的博客文章或者Agner Fog的这个投诉 ,以及他的错误报告的答案。

除了Pascal的良好答案(我确定这是主要动机)之外,还有可能某些处理器在有符号整数溢出时引发exception,这当然会导致编译器不得不“安排其他行为”的问题(例如使用额外的指令来检查潜在的溢出,并在这种情况下计算不同)。

还值得注意的是,“未定义的行为”并不意味着“不起作用”。 这意味着在这种情况下允许执行任何事情。 这包括做“正确的事情”以及“报警”或“崩溃”。 大多数编译器在可能的情况下会select“做正确的事情”,假设相对容易定义(在这种情况下)。 但是,如果计算中出现溢出问题,理解实际产生的结果非常重要,编译器可以执行除期望之外的任务(并且这可能取决于编译器版本,优化设置等) 。

除了上面提到的其他问题,无符号math换行使得无符号整数typesperformance为抽象代数组(这意味着,除了别的以外,对于任何一对值XY ,都会存在一些其他值Z ,使得X+Z如果投掷得当, X+Z将等于YYZ ,如果投掷得当,则等于X )。 如果无符号值仅仅是存储位置types而不是中间expression式types(例如,如果没有最大整数types的无符号等价物,并且对无符号types的算术运算performance得好像它们首先将它们转换为更大的有符号types那样对定义的包装行为没有太多需要,但是在没有例如加法逆的types中进行计算是困难的。

这有助于环绕行为实际上有用的情况,例如TCP序列号或某些algorithm(如散列计算)。 这也可能有助于检测溢出的情况,因为执行计算和检查溢出是否比预先检查是否会溢出更容易,特别是如果计算涉及最大的可用整数types。

首先,请注意C11 3.4.3,像所有例子和脚注,都不是规范性的文字,因此与引用无关!

表示整数和浮点溢出的相关文本是未定义的行为是这样的:

C11 6.5 / 5

如果在评估expression式时发生了exception情况(也就是说,如果结果没有在math上定义,或者不在其types的可表示值的范围内),则行为是不确定的。

有关无符号整数types的行为的澄清具体可以在这里find:

C11 6.2.5 / 9

有符号整数types的非负值的范围是相应的无符号整数types的子范围,并且每种types中相同值的表示是相同的。 涉及无符号操作数的计算永远不会溢出,因为无法用结果的无符号整数types表示的结果被减less的模数大于可以由结果types表示的最大值的数。

这使得无符号整数types成为特殊情况。

另请注意,如果任何types转换为带符号types,并且旧值不能再表示,则会出现exception。 这个行为只是实现定义的,尽pipe可能会引发一个信号。

C11 6.3.1.3

6.3.1.3有符号和无符号整数

当整数types的值被转换为除_Bool以外的另一个整数types时,如果该值可以由新types表示,则该值不变。

否则,如果新types是无符号的,则通过重复地增加或减去新types中能够表示的最大值之一来转换该值,直到该值在新types的范围内。

否则,新的types被签名并且值不能被表示; 结果是实现定义的或实现定义的信号被引发。

也许为什么无符号算术定义的另一个原因是因为无符号数形成整数模2 ^ n,其中n是无符号数的宽度。 无符号数字只是用二进制数字而不是十进制数字表示的整数。 在模量系统中执行标准操作是很好理解的。 我相信OP的报价是指这个事实。

无符号数字通常使用二进制补码表示。 它们使得某些操作在二进制格式中更有意义。 例如,递增负数与正数相同(期望在溢出条件下)。 机器级别的一些操作对于已签名和未签名的数字可以相同。 但是,在解释这些行动的结果时,有些情况是没有意义的 – 积极和消极的溢出。