2012-02-15 25 views
2

我有以下示例代码:按位变速操作uint64_t中可变

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

其结果将是:

x=0xfffffffff0ffffff 
y=0xfffff 

任何人都可以解释不同?为什么x在64位上计算,而y只在32上计算?

回答

5

默认操作是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; 
+0

要挑剔,默认值是(有符号)'int',不管它是什么。 – Lundin 2012-02-15 09:57:52

+0

严格来说,32位系统的0xFF << 24是未定义的行为,其结果可能是任何东西。 – Lundin 2012-02-15 10:52:29

+0

我不能同意。行为应该是可预测的。如果'a'的类型是'char',那么0xff << 24将产生负值;否则,如果'a'的类型为short或int,则它将是一个正值。 – ciphor 2012-02-15 12:29:35

2

0x0f << 24由于是作为int观察时的正数,它是符号扩展到一个正数,即,0x00000000_0f000000(下划线是只是为了可读性,C不支持此语法)。然后这转变成你所看到的。

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

+0

严格来说,32位系统的0xFF << 24是未定义的行为,其结果可能是任何东西。 – Lundin 2012-02-15 10:52:15

0

你已经在你的程序中没有定义,所以任何可能发生的事情。

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

ISO 9899:2011 6.5.7/4:

“E1的结果< < E2是E1左移E2的位位置;腾空 比特以零填充”/-/

“如果E1有一个签名的类型和非负的值,E1×2E2是结果类型表示的, 然后就是所得到的值;否则,该行为是 未定义。

因此不能使用表达式0xFF < < 24。此后该程序可以自由打印任何垃圾值。

但是,如果我们忽略了一个专注于为0x0F < 24:

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

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

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