【问题标题】:Shifting, types and sign extensions in CC 中的移位、类型和符号扩展
【发布时间】:2014-05-28 00:45:08
【问题描述】:

我有以下代码:

unsigned char chr = 234; // 1110 1010
unsigned long result = 0;
result = chr << 24;

现在结果将等于 18446744073340452864,即二进制的 1111 1111 1111 1111 1111 1111 1111 1111 1110 1010 0000 0000 0000 0000 0000 0000

当 chr 未签名时,为什么要进行符号扩展?

此外,如果我将移位从 24 更改为 8,则结果为 59904,即二进制的 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1110 1010 0000 0000。为什么这里没有扩展? (任何 23 或更少的班次都没有对其进行符号扩展)

在我当前的平台上,sizeof(long) 也是 8。

转换时自动转换为更大尺寸类型的规则是什么?在我看来,如果移位是 23 或小于 chr 被强制转换为无符号类型,如果它是 24 或更大,它被强制转换为有符号类型? (以及为什么用左移来完成符号扩展)

【问题讨论】:

  • 你如何打印/读取值?
  • 使用 gdb 的打印。但这不是打印的问题,因为我在结果上使用了按位 OR 并且由于所有前导 1 而它搞砸了。
  • 我使用codepad.org/zHW9scz4 到11101010 << 24 = 0000000000000000000000000000000011101010000000000000000000000000(没有符号扩展)
  • 简短的版本是积分促销将chr更改为int,然后应用移位(通过移位到符号位引起UB)。
  • @MattMcNabb,为什么不将其提升为 unsigned int 因为 chr 是未签名的

标签: c types shift sign-extension


【解决方案1】:

使用chr = 234,表达式chr &lt;&lt; 24 被单独评估:chr 提升为(32 位有符号)int 并左移 24 位,产生负的int 值。当您分配给 64 位 unsigned long 时,符号位将通过 64 位值的最高有效 32 位传播。请注意,chr &lt;&lt; 24 的计算方法本身不受赋值对象的影响。

当移位仅为 8 位时,结果是一个正(32 位有符号)整数,并且该符号位 (0) 通过unsigned long 的最高有效 32 位传播。

【讨论】:

    【解决方案2】:

    要理解这一点,最容易从的角度来思考。

    每个整数类型都有一个固定范围的可表示值。例如,unsigned char 的范围通常从 0255 ;其他范围也是可能的,您可以通过在limits.h 中检查UCHAR_MAX 来找到编译器的选择。

    在进行整数类型之间的转换时;如果 value 在目标类型中是可表示的,那么转换的结果就是那个值。 (这可能是不同的位模式,例如符号扩展)。

    如果该值在目标类型中不可表示,则:

    • 对于 已签名 目标,行为由实现定义(可能包括发出信号)。
    • 对于 unsigned 目标,该值以类型中可表示的最大值加一为模进行调整。

    现代系统通过左截断过多位来处理有符号超出范围的赋值;如果它仍然超出范围,那么它会保留相同的位模式,但值会更改为该位模式在目标类型中表示的任何值。


    转到您的实际示例。

    在 C 中,有一种叫做整体提升的东西。对于&lt;&lt;,这发生在左侧操作数上;对于算术运算符,它发生在所有操作数上。整数提升的效果是任何小于int 类型的值都将转换为与int 类型相同的值。

    此外,&lt;&lt; 24 的定义是乘以 2^24(其中 this 具有提升的左操作数的类型),如果 this 溢出则具有未定义的行为。 (通俗地说:移入符号位会导致 UB)。

    因此,明确地进行所有转换,您的代码是

    result = (unsigned long) ( ((int)chr) * 16777216 )
    

    现在,这个计算的结果是3925868544,如果你在一个典型的32位系统上int,它大于INT_MAX,即2147483647,所以行为未定义。

    如果我们想在典型系统上探索这种未定义行为的结果:可能发生的情况与我之前概述的超出范围分配的过程相同。 3925868544 的位模式当然是1110 1010 0000 0000 0000 0000 0000 0000。将其视为 int 的模式,使用 2 的补码给出 int -369098752

    最后我们将此值转换为unsigned long-369098752 超出了unsigned long 的范围;无符号目的地的规则是调整值以 ULONG_MAX+1 为模。所以你看到的值是18446744073709551615 + 1 - 369098752

    如果您的意图是以unsigned long 精度进行计算,则需要创建操作数之一unsigned long;例如做((unsigned long)chr) &lt;&lt; 24。 (注意:24ul 不起作用,&lt;&lt;&gt;&gt; 的右侧操作数的类型不会影响左侧操作数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-28
      • 2013-10-16
      • 2017-07-20
      • 1970-01-01
      相关资源
      最近更新 更多