我知道 C 语言中的以下代码:

(1 << 31)

根据 C 标准 ( ),被视为未定义行为:

如果右操作数为负或者其值大于 int 中的位数,则移位运算的结果未定义。

我只是好奇以下 C 代码是否:

(1LL << 63)

在任何 C 标准中也被视为未定义行为。

7

  • 2
    您正在阅读 DEC C 语言的文档。您对“通用”C 感兴趣,还是特别对 DEC C 感兴趣?我相信 DEC C 已经很久了。


    – 


  • 一般标准是指“左操作数的宽度”,而不是“一个整数”。可能与


    – 


  • 1
    1无论有符号变量的大小是多少,将 a 左移到 MSB(符号位)都是未定义的行为


    – 

  • @KamilCuk DEC C 标准被用作示例。我对“通用 C”感兴趣


    – 

  • @Dunes 我不认为这是重复的。答案指出,“如果右操作数的值为负数或大于或等于提升后的左操作数的宽度,则行为未定义。”但是,我的问题仍然存在,因为 63 不是“大于或等于提升后的左操作数的宽度”,而是 64。


    – 



最佳答案
3

C23说(我强调):

的结果E1 << E2E1左移的E2位位置;空出的位用零填充。如果E1具有无符号类型,则结果值为E1× 2ᴱ²,回绕。如果E1具有有符号类型和非负值,并且E1×2ᴱ²可在结果类型中表示,则这就是结果值;否则,行为未定义。

因此,如果您的平台long long可以表示 2⁶³,那就没问题。由于必须使用一位来指示其符号,这意味着如果有 64 位,1LL << 63则是 UB 。long long

long long不能小于 64 位,但可以更宽)。

3

  • 那么,答案是什么? (1 << 31) 绝对是未定义的行为,无论在哪个平台上编译。根据 C 标准,long long 至少包含 64 位,而 int 至少包含 32 位。引用听起来含糊不清。 (1 << 31)“可在结果类型中表示”,但结果在不同平台上的行为是未定义的行为 🙂


    – 

  • 3
    否,如果可表示(例如,如果目标的类型为 33 位或更多位宽),1 << 31则为定义的行为。同样,只有当您的类型为 65 位或更多位时,才是定义的行为。 2³¹int1LL << 63long long


    – 

  • 2
    @lollol:回复“(1 << 31)绝对是未定义的行为,无论它在哪个平台上编译”:这是错误的。如果超过 32 位1 << 31则定义。int


    – 

有几件事需要考虑(C17 6.5.7):

对每个操作数执行整数提升。结果的类型是提升后的左操作数的类型。如果右操作数的值为负数或大于或等于提升后的左操作数的宽度,则行为未定义。

E1 E2 的结果<<是 E1 左移 E2 位;空出的位用零填充。如果 E1 具有无符号类型,则结果值为 E1 × 2 E2,以结果类型可表示的最大值多 1 为模数。如果 E1 具有有符号类型和非负值,并且 E1 × 2 E2可在结果类型中表示,则这就是结果值;否则,行为未定义。

  • 在 6.2.6 中,有符号类型的宽度E1正式定义为值位加上符号位。这意味着对于类型的有符号变量intn_t,我们最多可以移动。如果我们移动的次数超过这个数字,无论是否包含负值,E1 << n我们都会调用未定义的行为 。E1

    因此,从有符号类型中“移出”数据是无效的;此代码可能有 UB:
    ms_byte_cleared = (my_signed32 << 40) >> 8;

  • 如果E1保留负值,则左移总是会引发未定义的行为。

  • 如果移位导致的值大于提升的操作数所能容纳的值(本质上如果我们最终“将数据移位到符号位”),那么这也是未定义的行为。

long long但是 C 标准并未定义a 的宽度。我们只知道它至少足够大以容纳值+9223372036854775807 (2 63 -1)(定义在 C17 5.2.4.2.1 中)。因此,我们不能严格地说它(1LL << 63)肯定是 UB。但是我们可以说它(int64_t)1 << 63是 UB,因为它得到的值是 2 63,大于 an 的最大值int64_t2 63 -1。

(1LL << 62) 将得出4611686018427387904d100000000000000000000000000000000000000000000000000000000000000b。这是 63 位。

(1LL << 63) 将得出-9223372036854775808d-1000000000000000000000000000000000000000000000000000000000000000n。这是二进制补码表示,是 64 位有符号整数可表示的最小负值。这是 64 位。

(1LL << 64) 将给出0d0b,这将被视为未定义的行为。

2

  • 2
    您描述的是可能的行为,但标准明确指出,在使用 64 位二进制补码的目标上,这是未定义的- 请参阅我的答案中的第 6.5.7 节。


    – 

  • 我明白了。顺便说一下,这本书不错。


    –