我在C中使用整数,试图探索更多关于何时以及如何发生溢出的信息。我注意到,当我把两个正数相加时,其和溢出,我总是得到一个负数。另一方面,如果我把两个负数相加,其和溢出,我总是得到一个正数(包括0)。我做了一些实验,但我想知道这是否对每一个案例都是正确的。
ao218c7q1#
在C语言中,溢出是未定义的行为。C说一个涉及整数的表达式 * 溢出 *,如果它在通常的算术转换之后的结果是有符号类型的,并且不能用结果的类型表示。赋值和强制转换表达式是一个例外,因为它们由整数转换控制。无符号类型的表达式不能溢出;例如,0U - 1是UINT_MAX。示例如下:
0U - 1
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)利用整数溢出作为未定义行为来执行各种类型的优化。举例来说:
gcc
a - 10 < 20
当a在升级后是int类型时,表达式在gcc中减少(当启用优化时)为:
a
int
a < 30
它利用了当a在INT_MIN + 10 - 1到INT_MIN的范围内时表达式的未定义行为。当a是unsigned int时,无法进行此优化,因为如果a是0,则a - 10必须被评估为UINT_MAX - 9(没有未定义的行为)。优化a - 10 < 20到a < 30将导致与a是0到9时所需的结果不同的结果。
INT_MIN + 10 - 1
INT_MIN
unsigned int
0
a - 10
UINT_MAX - 9
9
wgxvkvu92#
有符号整数的溢出在C中是未定义的行为,因此没有保证。也就是说,回绕或算术模2N,其中N是类型中的位数,是一种常见的行为。对于这种行为,实际上,如果一个和溢出,结果具有相反的符号的操作数。
N
lokaqttq3#
形式上,有符号算术在溢出时的行为是未定义的;任何事情都可能发生,而且是“正确的”。这与无符号算术相反,后者完全定义了溢出。在实践中,许多旧的编译器使用带符号的算术,如你所描述的那样溢出。然而,现代GCC正在改变它的工作方式,依赖这种行为是非常不明智的。当编译代码的环境中的任何东西发生变化时,它可能会随时发生变化-编译器,平台...
0s7z1bwu4#
C中的Overflow是一个可怕的混乱。
更糟糕的是这与整数提升的交互方式。多亏了推广,你可以在看起来像是在做无符号算术的时候做有符号算术。例如,考虑以下代码
uint16_t a = 65535; uint16_t b = a * a;
在16位int的系统上,这段代码是定义良好的。然而,在一个32位int的系统上,乘法将以有符号int的形式发生,产生的溢出将是未定义的行为!
4条答案
按热度按时间ao218c7q1#
在C语言中,溢出是未定义的行为。
C说一个涉及整数的表达式 * 溢出 *,如果它在通常的算术转换之后的结果是有符号类型的,并且不能用结果的类型表示。赋值和强制转换表达式是一个例外,因为它们由整数转换控制。
无符号类型的表达式不能溢出;例如,
0U - 1
是UINT_MAX
。示例如下:
永远不要让任何整数表达式溢出;现代编译器(如
gcc
)利用整数溢出作为未定义行为来执行各种类型的优化。举例来说:
当
a
在升级后是int
类型时,表达式在gcc
中减少(当启用优化时)为:它利用了当
a
在INT_MIN + 10 - 1
到INT_MIN
的范围内时表达式的未定义行为。当
a
是unsigned int
时,无法进行此优化,因为如果a
是0
,则a - 10
必须被评估为UINT_MAX - 9
(没有未定义的行为)。优化a - 10 < 20
到a < 30
将导致与a
是0
到9
时所需的结果不同的结果。wgxvkvu92#
有符号整数的溢出在C中是未定义的行为,因此没有保证。
也就是说,回绕或算术模2N,其中
N
是类型中的位数,是一种常见的行为。对于这种行为,实际上,如果一个和溢出,结果具有相反的符号的操作数。lokaqttq3#
形式上,有符号算术在溢出时的行为是未定义的;任何事情都可能发生,而且是“正确的”。这与无符号算术相反,后者完全定义了溢出。
在实践中,许多旧的编译器使用带符号的算术,如你所描述的那样溢出。然而,现代GCC正在改变它的工作方式,依赖这种行为是非常不明智的。当编译代码的环境中的任何东西发生变化时,它可能会随时发生变化-编译器,平台...
0s7z1bwu4#
C中的Overflow是一个可怕的混乱。
更糟糕的是这与整数提升的交互方式。多亏了推广,你可以在看起来像是在做无符号算术的时候做有符号算术。例如,考虑以下代码
在16位int的系统上,这段代码是定义良好的。然而,在一个32位int的系统上,乘法将以有符号int的形式发生,产生的溢出将是未定义的行为!