【问题标题】:Is INT_MAX + (-1) an undefined behavior?INT_MAX + (-1) 是未定义的行为吗?
【发布时间】:2026-01-30 14:00:01
【问题描述】:

在 C++ 中,有符号类型的溢出是未定义的行为。以下示例是否也是未定义的行为?

#include <limits.h>

int f() {
  int a = INT_MAX;
  int b = -1;
  return a + b;
}

这不是数学上下文中的溢出,但 CPU 会看到它可能像 add 0x7fffffff 0xffffffff.

【问题讨论】:

  • 您不能谈论 语言 意义上的未定义行为并将其与 CPU 的观点混为一谈。不,这不是未定义的行为,因为没有溢出。
  • @joemartin94 听说过涡轮增压器吗?
  • 2,147,483,647 + (-1) = 2,147,483,646,就像 5 + (-1) = 4。
  • @joemartin94 但是,你是完全正确的 OP 应该选择一个。
  • 我就是不明白这个问题,为什么会是UB。你甚至可以问,-1 + -1 是不是 UB?毕竟是0xffffffff + 0xffffffff。当然不是UB。

标签: c++ undefined-behavior integer-overflow


【解决方案1】:

你给出的例子不是溢出。

来自* (https://en.wikipedia.org/wiki/Integer_overflow):

...当算术运算试图 创建一个超出范围的数值 用给定的位数表示——要么大于 最大或低于最小可表示值。

INT_MAX + (-1) 不超出int类型可表示的范围,定义结果。

【讨论】:

    【解决方案2】:

    由于INT_MAX + (-1)的结果在int的可表示值范围内,所以定义明确

    您应该停止将语言视为汇编或机器代码之上的薄层。

    关于程序的未定义性,没有 CPU,只有运行程序的abstract machine

    【讨论】:

      【解决方案3】:

      在二进制补码系统中,进位和溢出是不同的概念。许多这样的系统被设计成支持多字算术。如果将值解释为无符号的算术和将超出类型的范围,则会报告进位。任何高位进位与进位不同的计算后都会报告溢出,但只有在计算高位字节后才有意义。

      例如,在 8 位系统上,“int”通常为两个字节,INT_MAX 为 0x7F:0xFF,-1 为 0xFF:FF。加法是通过将两个低字节相加,然后将高两个字节与低位进位相加。

      • 将 0xFF 添加到 0xFF 会产生 0xFE 和进位,但不会溢出(进出高位)。

      • 将 0x7F 加到 0xFF 得到 0x7E,没有进位也没有溢出(高位既没有进位也没有进位)。

      这些细节对于 C 程序员来说通常无关紧要,因为编译器通常不提供访问溢出标志的方法,也不提供任何方法来确保以使标志在任何特定点有意义的方式执行计算代码。尽管如此,C89 的基本原理说明:

      C 代码可能是不可移植的
      尽管它努力让程序员有机会编写真正可移植的程序,但 C89 委员会并不想强迫程序员编写 可移植性,以排除将 C 用作“高级汇编程序”:编写特定于机器的代码的能力是 C 的优势之一。正是这一原则在很大程度上促使区分严格符合程序和符合程序(§4)。

      注意,顺便说一句,将小型无符号类型提升为有符号 int 的理由强烈暗示 C89 的作者期望某些东西 像下面这样在普通平台上应该是安全的:

      unsigned mul_mod_65536(unsigned short x, unsigned short y)
      { return (x*y) & 0xFFFF; }
      

      尽管如此,如果在普通 32 位平台上处理此类代码时,如果由 gcc 等过度“聪明”的优化编译器处理,这些代码可能会出现故障。为此类函数生成的机器代码没有理由关心x*y 的值是否超过INT_MAX,因为结果的高位将被截断,而低位在任何情况下都是正确的.但是,在某些情况下,如果 gcc 知道,例如y 将是 65535,它可能会决定由于 x*y“不能”超过 INT_MAX,x“不能”超过 0x8000。标准的作者可能不想排除 C 被用作高级汇编程序的可能性,但这并不意味着编译器作者也有这种感觉。

      【讨论】: