左右移位负整数是否定义了行为?

时间:2011-12-07 13:15:52

标签: c++ undefined-behavior bit-shift

我知道,正确移位负签名类型取决于实现,但如果我执行左移怎么办?例如:

int i = -1;
i << 1;

这个定义明确吗?

我认为标准没有说明带有签名类型的负值

  

如果E1具有带符号类型且非负值,则E1×2 E2 为   在结果类型中可表示,那么这就是结果值;   否则,行为未定义。

它只阐明如果结果在签名类型中无法表示,则行为未定义。

3 个答案:

答案 0 :(得分:28)

你没有正确地阅读那句话。标准定义如果:左操作数具有带符号类型非负值结果可表示(以前在同一段落中为无符号类型定义它)。在所有其他情况下(注意该句中的use of the semicolon),即,如果未验证任何这些条件,则行为未定义。

答案 1 :(得分:15)

当C标准被编纂时,不同的平台在左移负整数时会做不同的事情。在其中一些中,行为可能会触发特定于实现的陷阱,其行为可能在程序的控制之外,并且可能包括随机代码执行。尽管如此,为这些平台编写的程序可能会利用这种行为(程序可能会指定用户在运行之前必须执行某些操作来配置系统的陷阱处理程序,但程序可能会利用此行为适当配置的陷阱处理程序)。

C标准的作者不想说必须修改负数左移的机器的编译器以防止这种陷阱(因为程序可能依赖于它),但是如果左移允许负数触发陷阱,这可能导致任意行为(包括随机代码执行),这意味着允许左移一个负数做任何事情。因此,未定义的行为。

在实践中,直到大约5年前,99%以上的编译器为使用二次补码数学的机器编写(意味着自1990年以来制造机器的99%以上)将始终为x<<y产生以下行为:和x>>y,在某种程度上,代码依赖这种行为被认为不再比假定char为8位的代码更不可移植。 C标准没有强制要求这样做,但是任何想要与广泛的现有代码兼容的编译器作者都会遵循它。

  • 如果y是签名类型,则会x << yx >> y进行评估,就像y被转换为无符号一样。
  • 如果x的类型为int,则x<<y相当于(int)((unsigned)x << y)
  • 如果x的类型为int,则为x>>y,等同于(unsigned)x >> y。如果x的类型为int且为负数,则x>>y相当于`〜(〜((unsigned)x)&gt;&gt; y)。
  • 如果x的类型为long,则适用类似的规则,但适用unsigned long而不是unsigned
  • 如果x是N位类型且y大于N-1,那么x >> yx << y可以任意产生零,或者可能就像右手操作数为y % N;它们可能需要与y成比例的额外时间[请注意,在32位计算机上,如果y为负数,则可能是时间,但我只知道一台机器在实践中运行超过256个额外的步骤]。编译器在选择时不一定一致,但总会返回其中一个指示值而没有其他副作用。

遗憾的是,由于某些原因我无法理解,编译器编写者已经决定,编程器不应该允许程序员指出编译器应该使用什么假设来删除死代码,而应该假设不可能执行其行为不存在的任何移位。不受C标准的约束。因此,给出如下代码:

uint32_t shiftleft(uint32_t v, uint8_t n)
{
  if (n >= 32)
    v=0;
  return v<<n;
}

编译器可以确定,因为当n为32或更大时代码将参与未定义行为,编译器可能会认为if永远不会返回true,因此可能省略代码。因此,除非或者直到有人提出C标准来恢复经典行为并允许程序员指定哪些假设值得删除死代码,否则不建议将此类结构用于任何可能提供给超现代编译器的代码。 / p>

答案 2 :(得分:3)

  

否则,行为未定义。

这包括

  

如果E1的签名类型为且非负值