编译器在表达式评估中的差异

时间:2014-07-25 10:33:37

标签: c compiler-construction compiler-errors

我已经实现了以下函数来模拟if-then-else:

int foo(int x, int y, int z) {
  int negOne = (1<<31)>>31;
  int test = !(~x + !x) + negOne;

  int ans = (test & y) | (~test & z);

  return ans;
}

我有一些限制,顶部有点像黑客,但它有效。 test仅计算为0或-1。

赋值使用特定的编译器,在x = 0,y = -2147483648和z = 2147483647的情况下,编译器声明我的代码返回-2147483648。

这是没有意义的,因为如果x = 0,则test = 0.如果test = 0,则ans表达式将计算为z,即2147483647。

我已经在两个不同的编译器上仔细检查了我的输出,它说我正在返回2147483647,正确的答案,所以我还要假设这是编译器错误,可能与整数的边界有关?当然,除非我的代码中存在错误。

还有其他编译器信息: 编译器称为dlc编译器。我之前有过解析错误&#34;在我的函数中间声明我的变量的问题,我被告知编译器可能是C89。将这些声明移到函数顶部可以解决问题。

更新: 将negOne表达式更改为~1 + 1并没有解决任何问题。 我评估了ans表达式的两面,并且正如预期的那样,它们分别评估为0和2147483647,所以这似乎也不是问题。快递最后将评估0 | 2147483647,这是2147483647;那是ans的值是什么,我还检查了接收函数返回值的变量的值,再次,它是2147483647.。

所以我仍然感到困惑的是为什么一个特定的编译器返回-2147483648。

2 个答案:

答案 0 :(得分:1)

如果int在您尝试此操作的计算机上为32位宽,则代码可能会显示未定义的行为。左转从标准C99§6.5.7(强调我的):

  

E1的结果&lt;&lt; E2是E1左移E2位位置;腾出的位用零填充。如果E1具有无符号类型,则结果的值为E1×2 E2 ,比结果类型中可表示的最大值减少一个模数。 如果E1具有有符号类型和非负值,并且E1×2 E2 在结果类型中可表示,那么这就是结果值;否则,行为未定义。

文字1是带符号的int,即此处的E1是有符号整数,并且具有非负值1。 1×2 31 = 2,147,483,648,在具有32位整数的机器上无法表示,因为这台机器上的signed int范围是-2,147,483,648到2,147,483,647。

当您在UB土地时,所有投注都会关闭,因此任何输出都是可能的。我不明白为什么你不能这样做:

int negOne = -1;

答案 1 :(得分:1)

我认为当你使用那个特定的编译器时会发生什么问题是simpe溢出。由于不严格指定某个顺序时,不同的编译器使用不同的操作顺序,因此该代码的输出不是确定性的。在特殊情况下,我认为添加会导致溢出。

这里描述了一些这类陷阱的好例子:http://www.fefe.de/intof.html

有时这些陷阱在编译之前通过编译器进行优化而无意中被避免。所以我建议你把你所使用的所有编译器的输出反汇编 - 然后直接出现溢出点,差异也很明显。

我建议您使用类似的东西进行条件仿真:

int foo(int x, int y, int z) {
  int test = ~(!!x) + 1;
  return (test & y) | (~test & z);
}

这应该避免在旧C标准中将x表示为“boolean”的溢出。