uint64_t变量中C的按位移位运算

时间:2012-02-15 09:30:34

标签: c 64-bit bit-manipulation

我有以下示例代码:

uint64_t x, y;
x = ~(0xF<<24);
y = ~(0xFF<<24);

结果将是:

x=0xfffffffff0ffffff
y=0xfffff

任何人都可以解释这个区别吗?为什么x是在64位上计算而y只在32位计算?

4 个答案:

答案 0 :(得分:6)

默认操作是32位。

x=~(0xf<<24);

此代码可以反汇编为以下步骤:

int32_t a;
a=0x0000000f;
a<<=24;   // a=0x0f000000;
a=~a;     // a=0xf0ffffff;
x=(uint64_t)a;  // x = 0xfffffffff0ffffff;

y = ~(0xFF<<24);

int32_t a;
a=0x000000ff;
a<<=24;   // a=0xff000000;
a=~a;     // a=0x00ffffff;
x=(uint64_t)a;  // x = 0x000000000ffffff;

答案 1 :(得分:2)

因为0x0f << 24在被视为int时是一个正数,所以它被符号扩展为正数,即0x00000000_0f000000(下划线只是为了可读性,C不是支持这种语法)。然后将其转换为您所看到的内容。

另一方面,

0xff << 24是否定的,所以它的符号扩展方式不同。

答案 2 :(得分:1)

其他海报已经说明了为什么会这样做。但要获得预期的结果:

uint64_t x, y; 
x = ~(0xFULL<<24); 
y = ~(0xFFULL<<24);

或者你可以这样做(我不知道这是否比上面的慢):

uint64_t x, y; 
x = ~(uint64_t(0xF)<<24); 
y = ~(uint64_t(0xFF)<<24); 

然后:

x = 0xfffffffff0ffffff
y = 0xffffffff00ffffff

答案 3 :(得分:0)

您的程序中存在未定义的行为,因此可能发生任何事情。

  • 整数文字0xF或0xFF的类型为int,相当于signed int。在这个特定的平台上,int显然是32位。
  • 整数文字24也是(带签名的)int
  • 当编译器评估&lt;&lt;操作,两个操作数都是(签名)int,因此不会发生隐式类型促销。 &lt;&lt;&lt;&lt;&lt;&lt;因此,操作也是(签名)int
  • 值0xF&lt;&lt;&lt; 24 = 0x0F000000作为非负值适合(带符号)int,所以一切正常。
  • 值(0xFF&lt;&lt;&lt; 24 = 0xFF000000 不适合(签名)int!在这里,调用未定义的行为,并且可能发生任何事情。

ISO 9899:2011 6.5.7 / 4:

  

“E1&lt; E2的结果是E1左移E2位位置;空出   位用零填充。“/ - /

     

“如果E1具有带符号类型和非负值,并且E1×2E2可在结果类型中表示,   那就是结果价值;否则,行为是   未定义。

因此不能使用表达式0xFF&lt;&lt; 24。该程序可以随后打印任何垃圾值。

但如果我们忽略那个并专注于0x0F <24:

  • 0x0F000000仍然是(已签名)int。 〜运算符适用于此。
  • 结果是0xF0FFFFFF,它仍然是一个有符号的int。几乎在任何系统上,这个32位十六进制等于二进制补码中的负数。
  • 在赋值期间,此signed int将转换为uint64_t类型。这分两步完成,首先将其转换为带符号的64位,然后将带符号的64转换为无符号64位。

这样的错误是为什么编码标准MISRA-C包含许多规则来禁止在这样的表达式中使用整数文字。符合MISRA-C的代码必须在每个整数文字后面使用u后缀(MISRA-C:2004 10.6),并且不允许代码对有符号整数执行按位运算(MISRA-C:2004 12.7)。