C整数溢出

C整数溢出,c,integer,overflow,C,Integer,Overflow,我在用C语言处理整数,试图探索溢出发生的时间和方式 我注意到,当我加上两个正数,其和溢出时,我总是得到一个负数 另一方面,如果我加上两个负数,其和溢出,我总是得到一个正数(包括0) 我做了一些实验,但我想知道这是否适用于所有情况。有符号整数的溢出在C中是未定义的行为,因此没有保证 也就是说,环绕运算或算术模2N是一种常见行为,其中N是类型中的位数。对于这种行为,确实如果一个和溢出,结果具有与操作数相反的符号。有符号整数的溢出在C中是未定义的行为,因此没有保证 也就是说,环绕运算或算术模2N是一种

我在用C语言处理整数,试图探索溢出发生的时间和方式

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

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


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

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


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

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


也就是说,环绕运算或算术模2N是一种常见行为,其中
N
是类型中的位数。对于这种行为,如果一个和溢出,结果的符号与操作数的符号相反。

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


实际上,许多较旧的编译器使用的是符号算术,正如您所描述的那样溢出。然而,现代GCC正在改变它的工作方式,如果你依赖它的行为,那将是非常不明智的。当编译代码的环境中的任何内容发生变化时,它可能会随时发生变化—编译器、平台等。正式地说,溢出时有符号算术的行为是未定义的;任何事情都可能发生,这是“正确的”。这与完全定义溢出的无符号算术形成对比


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

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

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

无符号类型的表达式不能溢出,它们是换行的,例如。g、 ,
0U-1
UINT\u 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
a
INT\u MIN+10-1
INT\u MIN
的范围内时,它利用表达式是未定义的行为


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

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

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

无符号类型的表达式不能溢出,它们是换行的,例如。g、 ,
0U-1
UINT\u 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
a
INT\u MIN+10-1
INT\u MIN
的范围内时,它利用表达式是未定义的行为


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

C中的溢出是一个非常糟糕的问题

  • 无符号算术或转换为无符号类型期间的溢出会导致包装模2n
  • 转换为有符号类型期间的溢出是实现定义的,大多数实现将包装模2n,但有些可能不包装模2n
  • 有符号算术期间的溢出是未定义的行为,根据标准,任何情况都可能发生。在实践中,有时它会做您不想做的事情,有时它会在编译器优化重要测试时在yoir代码中引起奇怪的问题
更糟糕的是,它如何与整数提升相互作用。多亏了晋升,当你看起来在做无符号算术时,你就可以做有符号算术了。例如,考虑下面的代码

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

在具有16位int的系统上,此代码定义良好。但是,在具有32位int的系统上,乘法将作为有符号int进行,并且产生的溢出将是未定义的行为

C中的溢出是一个非常糟糕的问题