【问题标题】:Does x ^= x & -x; where x is an unsigned integer invoke UB?x ^= x & -x;其中 x 是无符号整数调用 UB?
【发布时间】:2020-11-18 18:42:14
【问题描述】:

由于- 运算符应用于x(即unsigned),此函数是否会调用未定义的行为?我搜索了标准,找不到解释。

unsigned foo(unsigned x)
{
    return x ^= x & -x;
}

IMO 是的。

编辑
void func(unsigned x) 
{
    printf("%x", -x);
}

int main(void)
{
    func(INT_MIN);
}

IMO唯一的解释是它被提升为更大的有符号整数大小,然后转换为无符号。

如果提升为更大的整数大小,如果没有更大的有符号整数类型会怎样?

【问题讨论】:

标签: c language-lawyer


【解决方案1】:

这个表达式的行为是明确定义的。

允许使用类似于x = x + 1 的构造,因为在计算所有其他子表达式之前不会为x 分配值。在这种情况下也是如此。

-x 也没有问题,因为表达式具有无符号类型,因此具有明确定义的环绕行为而不是溢出。

C standard 的第 6.5.3.3p3 节关于一元 - 运算符指出:

一元- 运算符的结果是其(提升的)操作数的负数。对操作数执行整数提升,结果具有提升的类型。

因此,由于没有发生提升,因此类型在整个表达式中保持为unsigned。尽管标准中没有明确说明,-x 实际上与0 - x 相同。

对于INT_MIN被传递给这个函数的具体情况,它的类型是int,并且在unsigned的范围之外,所以它在传递给函数时会被转换。这导致有符号值 -2,147,483,648 被转换为无符号值 2,147,483,648(在二进制补码中恰好具有相同的表示,即 0x80000000)。然后当 -x 被计算时,它会环绕产生 2,147,483,648。

【讨论】:

  • 我 100% 不相信,尤其是对于我问题中的最后一个案例。还是非常感谢。顺便说一句,这是我的个人记录-8。仍然明白为什么它如此激进地被 DV-ted。
  • @P__JsupportswomeninPoland 因为INT_MIN 的类型为int,所以不会进行促销,将其传递给函数会导致转换为unsigned int
【解决方案2】:
6.2.5 类型
...
9 有符号整数类型的非负值范围是 对应的无符号整数类型,以及每个中相同值的表示 类型相同。41)涉及无符号操作数的计算永远不会溢出, 因为无法由生成的无符号整数类型表示的结果是 以比最大值大一的数字为模减少 由结果类型表示。
41) 相同的表示和对齐要求意味着可互换性 函数的参数、函数的返回值以及联合的成员。
...
6.3 转换
...
6.3.1.3 有符号和无符号整数

1 当整数类型的值转换为_Bool以外的其他整数类型时,如果 该值可以用新类型表示,它是不变的。

2 否则,如果新类型是无符号的,则通过反复加或 比新类型可以表示的最大值多减一 直到值在新类型的范围内。60)

3 否则,新类型是有符号的,值不能在其中表示;无论是 结果是实现定义的或引发了实现定义的信号。
60) 规则描述的是数学值的算术运算,而不是给定类型表达式的值
...
6.3.1.8 常用算术转换
...
否则,如果具有无符号整数类型的操作数的秩大于或 等于另一个操作数的类型的等级,然后 操作数与 有符号整数类型转换为无符号操作数的类型 整数类型。

简而言之 - -x 不会导致未定义的行为。表达式的结果仍然是无符号的,它只是映射到一个定义明确的非负值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-03
    • 2017-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-03
    相关资源
    最近更新 更多