C中有符号和无符号整数的按位非

bitwise NOT of signed and unsigned integers in C

我正在调试一个问题,发现以下代码段(已清理)是问题的原因。

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

我比较了生成的程序集并看到了这个

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

为什么 unsigned longNOT operator 会导致编译器将 0 与更高的字节而不是 ffffffff.

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

假设 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 的符号位。