繁体   English   中英

中间算术表达式中的整数溢出

[英]Integer overflow in intermediate arithmetic expression

这可能是一个非常基本的编程问题,但这是我想了解一段时间的问题。

考虑以下简单示例:

int main(void) 
{
  unsigned char a = 5;
  unsigned char b = 20;
  unsigned char m = 0xFF;

  unsigned char s1 = m + a - b;
  unsigned char s2 = m - b + a;
  printf("s1 %d s2 %d", s1, s2);
  return 0;
}

给定算术运算符在C中从左到右求值,此处的第一个计算应在m + a处溢出。 但是,运行该程序将为s1和s2返回相同的答案。 我的问题是:由于溢出,第一个表达式会导致未定义的行为吗? 第二个表达式应该避免溢出,但是我想了解为什么两个表达式返回相同的答案。

由于C的整数提升,s1计算有效地执行为:

unsigned char s1 = (unsigned char)( (int)m + (int)a - (int)b );

而且没有临时溢出。

(更正)对整数类型进行算术运算时,所有小于int的类型在计算过程中都会提升为int,如果结果类型较小则将其截断。

看到:

https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+理解+整数+转换+规则

根据ISO C规范§6.2.5.9

涉及无符号操作数的计算永远不会溢出,因为无法用所得的无符号整数类型表示的结果的模数要比该所得类型可以表示的最大值大一模。

这意味着似乎分别在加法和减法中出现的可能的正溢出和负溢出实际上都是按有符号int执行的,因此它们都是定义明确的。 对表达式求值后,结果将被截断为unsigned char因为这是左侧的结果类型。

通过将结果转换为int ,进行计算,然后将结果转换回原始类型,可以执行小于int类型的操作。 对于小型无符号类型, 只要计算结果适合int类型 ,这将导致静默忽略结果的高位。 该标准的已公布基本原理表明,作者希望非古旧的实现在将值存储到不大于int的无符号类型中时会忽略高位,而无需考虑计算是否适合int类型,但是“现代”编译器以这种方式可靠地运行已不再流行。 例如,在具有16位short和32位int的系统上,该函数

unsigned mulMod65536(unsigned short x, unsigned short y)
{ return (x*y) & 0xFFFFu; }

通常表现为以下行为:

unsigned mulMod65536(unsigned short x, unsigned short y)
{ return (1u*x*y) & 0xFFFFu; }

但是在某些情况下,gcc会基于x*y超过2147483647的情况以任意方式运行的事实做出“明智的”优化,即使没有理由高位比特会影响结果。

涉及小的有符号类型的操作与使用无符号类型的操作相似,不同之处在于,允许实现以实现定义的方式将超出较小类型范围的值映射到那些类型的值,或者如果尝试执行,则引发实现定义的信号。转换为超出范围的值。 实际上,即使在这种情况下,几乎所有实现都使用二进制补码截断。 尽管在某些情况下某些其他行为可能会更便宜,但该标准要求实现以一致的记录方式进行行为。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM