2017-03-28 110 views
1

我在调试一个问题,并发现下面的代码片段(消毒)是问题的原因。按位不是有符号整数和无符号整数在C

uint64_t testflag = 0; 
testflag &= ~(0x08ul); //bug 
testflag &= ~(0x08l); //expected 

我比较生成的程序集和看到这

uint64_t testflag = 0; 
804851e: c7 45 d8 00 00 00 00 movl $0x0,-0x28(%ebp) 
8048525: c7 45 dc 00 00 00 00 movl $0x0,-0x24(%ebp) 
    testflag &= ~(0x08ul); 
804852c: 83 65 d8 f7    andl $0xfffffff7,-0x28(%ebp) 
8048530: 83 65 dc 00    andl $0x0,-0x24(%ebp) 
    testflag &= ~(0x08l); 
8048534: 83 65 d8 f7    andl $0xfffffff7,-0x28(%ebp) 
8048538: 83 65 dc ff    andl $0xffffffff,-0x24(%ebp) 

为什么的unsigned longNOT operator使编译器和0具有较高的字节,而不是ffffffff

在64位机器上,我的gcc版本是gcc (GCC) 4.9.2 (Red Hat 4.9.2)

+2

什么是'sizeof(unsigned long)'? – aschepler

+0

签名扩展名与零扩展名,因为在您的机器上long是32位。 –

回答

1

假设32位unsigned long/long ...

uint64_t testflag; 

0x08ul  --> 00 00 00 08 
~(0x08ul) --> FF FF FF F7 
some_uint32_t = FF FF FF F7 

testflag &= some_uint32_t; 
testflag = testflag & some_uint32_t 
testflag = testflag & (uint64_t) some_uint32_t 
testflag = testflag & (uint64_t) FF FF FF F7 (or 4,294,967,288) 
testflag = testflag & 00 00 00 00 FF FF FF F7 

转换一个32位无符号到uint64_t是一个简单地0延伸。


~(0x08l)

0x08l  --> 00 00 00 08 
~(0x08l) --> FF FF FF F7 
some_int32_t = FF FF FF F7 

testflag &= some_int32_t; 
testflag = testflag & some_int32_t 
testflag = testflag & (uint64_t) some_int32_t 
testflag = testflag & (uint64_t) FF FF FF F7 (or - 8) 
testflag = testflag & FF FF FF FF FF FF FF F7 (or 18,446,744,073,709,551,608) 

现在-8为uint64_t是非常接近的uint64_t的最大值。
使用2的补码,结果是OP的long的符号位被扩展。 @Marc Glisse