发生整数溢出时无符号整数和有符号整数的行为差异

Difference in behaviour of unsigned and signed integer when integer overflow occurs

本文关键字:整数 符号 溢出 无符号整数      更新时间:2023-10-16

阅读维基百科上关于整数溢出的文章

我不太明白为什么有符号整数的溢出会导致未定义行为,而无符号整数的溢出会导致环绕。为什么他们的行为会有所不同?

另一个问题:编程语言一般都有防止整数溢出的保护措施吗?

差异的主要原因是C和c++语言规范允许实现使用以下三种不同的有符号整数表示之一

  1. 1的补充
  2. <
  3. 2的补/gh>签署的

如果语言规范在有符号溢出的情况下规定了一些特定的行为(即优先考虑上述两种表示中的一种),它将迫使基于两种不受欢迎的表示的平台实现"重"有符号整数算术。这将是必要的,因为这些平台的自然机器级行为与语言标准所要求的行为不匹配。这些实现必须不断地监视有符号溢出,并调整结果以满足标准需求。

这将严重降低使用不受欢迎的符号表示的平台上的有符号整数算术的性能,当然,这在C和c++这样的语言中是完全不可接受的,当涉及到像整数算术这样的基本操作时,这些语言被设计成尽可能接近底层硬件。

行为是undefined(与*unspecified"相反)的原因是因为在整数运算期间有符号溢出的情况下,有些平台故意生成硬件异常。注意,该行为仅对通常由机器执行的算术运算没有定义。对于值转换,有符号溢出不会产生未定义的行为(该行为实际上是由实现定义的)。

对于无符号类型,它们在所有平台上的表示都是相同的,这意味着要求在所有平台上保持一致的行为不是问题。在概念上符合"模2^width"行为的封装是几乎所有已知二进制硬件平台上的自然行为。

  1. 因为语言就是这样定义的。它允许在更多种类的硬件上更容易地开发符合标准的实现(例如,具有饱和算法的dsp)。

  2. 取决于语言。一些硬件可以,你可以在你的程序中利用它。

C/c++处理整数溢出的方法是提供在你正在工作的机器上最快的行为,所以在某些机器上(这里假设16位有符号整数):

32766 + 2 == -32768

,但在某些机器上是:

32766 + 2 == 32767

对于其他机器,您可以设置trap值或任何CPU将执行的操作。

注意Java有完美定义的整数溢出,以实现"一次写入,到处运行"。

对于无符号整数——它们的大部分应用是位掩码、位域和数字操作(模操作、标识符)——正是你不想让它们未定义的操作。

有些编程语言有这样的安全措施,有些没有:

  • Python 3自动将溢出的值转换为long类型(任意大的整数)。

  • 在C/c++中,你必须自己检查溢出条件,climits (C)和limits (c++)头已经为每种类型定义了最大值和最小值。

  • 在x86汇编中编程-在FLAGS和EFLAGS寄存器中有CF(进位标志)和OF(溢出标志)来检查何时发生溢出

许多语言也有任意精度类型,以防您想避免溢出,但操作速度较慢,因为这些变量(理论上)可能和您的内存一样大。

在Java中,您只有无符号的intlong值,并且无论您运行它,它们的行为都是一致的。如果你给整数加1。MAX_VALUE你打赌的整数。MIN_VALUE(换行),如果从Long中减去1。MIN_VALUE你得到Long.MAX_VALUE

所以我不知道为什么unsigned值的行为在其他语言中是未定义的。