1

我在 C 中使用整数,试图更多地探索溢出发生的时间和方式。

我注意到当我添加两个正数时,总和溢出,我总是得到一个负数。

另一方面,如果我将两个负数相加,其和溢出,我总是得到一个正数(包括 0)。

我做了一些实验,但我想知道这是否适用于每种情况。

4

4 回答 4

7

整数溢出是 C 中未定义的行为。

C 表示一个涉及整数的表达式溢出,如果它在通常的算术转换之后的结果是有符号类型的并且不能以结果的类型表示。赋值和强制转换表达式是一个例外,因为它们由整数转换决定。

无符号类型的表达式不能溢出,它们会换行,例如0U - 1is UINT_MAX

例子:

INT_MAX + 1    // integer overflow
UINT_MAX + 1   // no overflow, the resulting type is unsigned
(unsigned char) INT_MAX // no overflow, integer conversion occurs 

永远不要让任何整数表达式溢出,现代编译器(如gcc)利用整数溢出作为未定义行为来执行各种类型的优化。

例如:

a - 10 < 20

whenaint提升后的类型,表达式在gcc(启用优化时)减少为:

a < 30

它利用表达式是未定义的行为,当a在范围内INT_MIN + 10 - 1INT_MIN

无法在ais时进行此优化,unsigned int因为如果ais 0,则a - 10必须将其评估为UINT_MAX - 9(没有未定义的行为)。优化a - 10 < 20toa < 30将导致与所需的结果不同的结果 when ais 0to 9

于 2012-09-09T01:49:25.217 回答
2

有符号整数的溢出在 C 中是未定义的行为,因此无法保证。

也就是说,环绕或算术模 2 N是类型中N的位数,这是一种常见行为。对于这种行为,实际上如果总和溢出,则结果具有操作数的相反符号。

于 2012-09-09T01:19:53.680 回答
1

形式上,有符号算术溢出的行为是未定义的;任何事情都可能发生,而且它是“正确的”。这与完全定义溢出的无符号算术形成对比。

在实践中,许多较旧的编译器使用有符号算术,如您所描述的那样溢出。然而,现代 GCC 正在改变它的工作方式,你会非常不明智地依赖这种行为。当编译代码的环境中的任何内容发生变化时,它可能随时发生变化——编译器、平台、...

于 2012-09-09T01:21:32.880 回答
0

C中的溢出是一个可怕的混乱。

  • 无符号算术或转换为无符号类型期间的溢出导致模 2 n换行
  • 转换为有符号类型期间的溢出是实现定义的,大多数实现将模 2 n包装,但有些可能不会。
  • 有符号算术期间的溢出是未定义的行为,根据标准,任何事情都可能发生。在实践中,有时它会做你不想做的事情,有时它会在 yoir 代码中导致奇怪的问题,因为编译器会优化重要的测试。

更糟糕的是这与整数提升的相互作用。多亏了提升,当看起来你正在做无符号算术时,你可以做有符号算术。例如考虑以下代码

uint16_t a = 65535;
uint16_t b = a * a;

在具有 16 位 int 的系统上,此代码是明确定义的。然而,在具有 32 位 int 的系统上,乘法将作为有符号 int 进行,由此产生的溢出将是未定义的行为!

于 2021-06-21T14:49:11.237 回答