在现有的“语言”(方言族群)中,C标准的编写描述通常会通过执行底层平台所做的任何操作来处理带符号整数溢出,将值截断为底层类型的长度(大多数平台都是这样做的),即使在其他平台上也会执行某些其他操作,或触发某种形式的信号或诊断。
在K&R的书《C程序设计语言》中,该行为被描述为“机器相关”。
虽然标准的作者们在已发表的原理文档中指出了一些情况,在这些情况下,他们希望常见平台的实现会以常见方式表现出来,但他们不想说某些操作在某些平台上具有定义行为而在其他平台上则没有。此外,将行为描述为“实现定义的”将会导致问题。考虑以下内容:
int f1(void);
int f2(int a, int b, int c);
int test(int x, int y)
{
int test = x*y;
if (f1())
f2(test, x, y);
}
如果整数溢出的行为被定义为“实现定义”,那么任何可能引发信号或具有其他可观察副作用的实现都需要在调用f1()之前执行乘法,即使乘积的结果将被忽略,除非f1()返回一个非零值。将其归类为“未定义行为”可以避免这样的问题。
不幸的是,gcc将把“未定义行为”的分类解释为一种邀请,以不受常规因果定律约束的方式处理整数溢出。例如,对于下面这样的函数:
unsigned mul_mod_32768(unsigned short x, unsigned short y)
{
return (x*y) & 0x7FFFu;
}
尝试使用大于INT_MAX/y的x值调用它可能会任意破坏周围代码的行为,即使函数的结果在任何观察方式下都不会被使用。
UINT_MAX
转换为int
类型进行赋值时,会发生溢出。这是IDB。当溢出实际上在计算表达式时发生,例如999999999*999999999
,那么就是UB了。 - Nate Eldredge999999999*999999999
的结果转换为int类型,这实际上是将其截断。 - Dan999999999*999999999
不是按照“进行数学运算,然后根据通常的转换规则执行转换”的定义方式。它的定义是“在概念上进行数学运算,但如果结果不能用类型int
表示,则 UB”。转换规则从未发挥作用。 - Nate Eldredgelong long int a = 999999999*999999999;
也是未定义行为。当*
运算符被评估时,未定义行为就会发生。你尝试对结果做什么并不相关。 - Nate Eldredge