假设我有一个来自外部来源的变量i
:
int i = get_i();
假设i
是INT_MIN
和两个补码表示,-i
是不定义的?
这取决于平台。 C支持负数的三种表示(参见C99标准的section 6.2.6.2):
使用一个补码和符号和幅度,-INT_MIN
被定义(并且等于INT_MAX
)。使用二进制补码时,它取决于符号位1和所有值位零的值是陷阱表示还是正常值。如果它是正常值,则-INT_MIN
溢出,导致未定义的行为(请参阅C99标准的section 6.5)。如果它是一个陷阱表示,-INT_MIN
等于INT_MAX
。
也就是说,大多数现代平台使用两个补码而没有陷阱表示,因此-INT_MIN
通常会导致未定义的行为。
平台可以选择定义行为,但C标准并不要求它们保证任何相关内容。虽然历史上微计算机编译器的行为相对一致,但-INT_MIN会产生INT_MIN,或者在某些情况下,行为类似于大于INT_MAX的值,因此反而使其具有追溯性地改变被否定的值的变得更加时髦。因此,给出:
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-ness来“优化”以下内容
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进入qq
和rr
,具体取决于q
是否为零。
否定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;