在C中移动,输入和签名扩展名

时间:2014-05-28 00:45:08

标签: c types shift sign-extension

我有以下代码:

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或更多,它会被转换为有符号类型? (为什么符号扩展甚至完成左移)

2 个答案:

答案 0 :(得分:3)

使用chr = 234,表达式chr << 24将被隔离评估:chr被提升为(32位有符号)int并向左移位24位,产生一个负值int。分配给64位unsigned long时,符号位将传播通过64位值的最高32位。请注意,计算chr << 24的方法本身并不受赋值的影响。

当移位仅为8位时,结果为正(32位带符号)整数,并且该符号位(0)通过unsigned long的最重要的32位传播。

答案 1 :(得分:3)

要理解这一点,最容易根据进行思考。

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

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

如果目标类型中的值无法表示,则:

  • 对于签名的目的地,行为是实现定义的(可能包括发出信号)。
  • 对于无符号目的地,该值以模式中可表示的最大值为模,并加一。

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


转到你的实际例子。

在C中,有一种称为整体促销的东西。对于<<,这发生在左侧操作数上;算术运算符会发生在所有操作数上。积分促销的效果是,任何小于int的类型的值都会转换为类型为int的相同值。

此外,<< 24的定义乘以2 ^ 24(其中它具有提升的左操作数的类型),如果溢出则具有未定义的行为。 (非正式地:移入符号位会导致UB)。

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

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

现在,此计算的结果为3925868544,如果您使用的是32位int的典型系统,则大于INT_MAX,即2147483647,因此行为未定义

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

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

如果您的目的是以unsigned long精度进行计算,则需要创建一个操作数unsigned long;例如做((unsigned long)chr) << 24。 (注意:24ul无法正常工作,<<>>的右侧操作数类型不会影响左侧操作数。