是否否定INT_MIN是未定义行为?

6

假设我有一个变量i,它来自外部来源:

int i = get_i();

假设iINT_MIN并采用二进制补码表示,那么-i是否未定义?

这个问题的第二个答案可能会对你有所帮助:https://dev59.com/C1_Va4cB1Zd3GeqPTG7E - Jean-Baptiste Yunès
如果您尝试生成一个无法表示其类型的 rvalue,则会出现未定义行为。 - user3528438
1
短答案:是的,这是未定义行为。下面的答案提供了很好的细节,但有点拐弯抹角。 - Apollys supports Monica
3个回答

15
这取决于平台。C支持三种负数表示法(请参见C99标准的6.2.6.2节):
- 二进制补码。 - 反码。 - 原码。
对于反码和原码,-INT_MIN已定义(等于INT_MAX)。对于二进制补码,它取决于使用带符号位1和所有数值位为零的值是陷阱表示还是正常值。如果是正常值,则-INT_MIN会溢出,导致未定义行为(请参见C99标准的6.5节)。如果是陷阱表示,则-INT_MIN等于INT_MAX
话虽如此,大多数现代平台使用没有陷阱表示的二进制补码,因此-INT_MIN通常会导致未定义行为。

最后一段对我来说不太清楚,我认为你想表达的是如果 INT_MIN == -INT_MAX 那么 -INT_MIN == INT_MAX,否则 -INT_MIN 是未定义行为。 - M.M
@M.M 我想说的是,在什么情况下 INT_MIN == -INT_MAX 才会成立,而且这种情况甚至在二进制补码平台上也可能成立(至少在理论上是这样)。 - nwellnhof
我花了一段时间在6.2.6.2和6.5的错误部分寻找“-INT_MIN”是未定义行为的声明。最终我找到了它(我想),在6.5-5项中,它说:“如果在表达式的评估过程中发生异常情况(即,如果结果在数学上未定义或不在其类型的可表示值范围内),则行为是未定义的。” - pnkfelix
1
@pnkfelix 这仅在 -INT_MIN 超出整数可表示的值范围时才有意义。如果您使用符号-大小或补码,则 -INT_MIN 在整数可表示的值范围内,因此 -INT_MIN 完全没有问题。当然,如果它溢出,如使用二补数时会发生,那么它是未定义的行为。 - CoffeeTableEspresso

5

平台可以选择定义行为,但C标准不要求它们保证任何内容。历史上,微型计算机编译器的行为相对一致,好像-INT_MIN会产生INT_MIN或者某些情况下产生一个行为类似于比INT_MAX大1的值,但现在更流行的是对被否定的值进行追溯地更改。因此,如果有:

int wowzers(int x)
{
  if (x != INT_MIN) printf("Not int min!");
  return -x;
}

一种超现代编译器可能会使用表达式“-x”来确定在执行前一个比较时,x不能等于INT_MIN,并因此无条件地执行printf操作。

顺便说一句,gcc 8.2会利用将INT_MIN取反的UB性质来“优化”以下内容。

int qq,rr;
void test(unsigned short q)
{
    for (int i=0; i<=q; i++)
    {
        qq=-2147483647-i;
        rr=qq;
        rr=-rr;
    }
}

将代码转换为无条件将-2147483647存储到qq和2147483647存储到rr。删除rr=-rr行将使代码将-2147483647或-2147483648存储到qqrr中,具体取决于q是否为零。


你是否有支持超现代编译器假设的证据? - Yatharth Agarwal
@YatharthAgarwal:我已经确定了一些情况,其中对某些参数值进行表达式求值会导致编译器推断出不会接收到这样的参数。我还没有确定今天的编译器中是否有特别会导致这种情况发生的INT_MIN否定情况,但像gcc这样的编译器的作者非常清楚地表示,他们不保证在标准未规定并且未明确记录的情况下的未来行为。 - supercat
1
@YatharthAgarwal:我添加了一个函数示例,其中在尝试否定等于INT_MIN的值时,在gcc 8.2中将影响另一个对象的存储值。 - supercat
哇,不错的例子! - Yatharth Agarwal
更有趣的是,如果将 q 改为 -2147483646-i,编译器的行为会变得更加有趣。生成的代码效率比将 rr=-rr 行替换为 rr=-(unsigned)rr 更低,因此编译器对 UB 的处理甚至不能提高代码效率。 - supercat

2

负INT_MIN是否未定义行为?

是的,当INT_MIN < -INT_MAX时,这非常常见(2的补码)。这是整数溢出

int i = get_i();

#if INT_MIN < -INT_MAX
if (i == INT_MIN) {
  fprintf(stderr, "Houston, we have a problem\n");
  // Maybe return or exit here.
}
#endif 

int j = -i;

Houston, we have a problem


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接