Python中踩坑:Python的按位取反运算符~与C语言中的~有何区别?

有如下 C 代码:

#include <stdio.h>

int main() { int x = 0x87654321; int mask = 0xFF;

printf("0x%08x\n", ~mask);
printf("0x%08x\n", x &amp; mask);    
printf("0x%08x\n", x ^ ~mask);   
printf("0x%08x\n", x | mask); 
return 0;

}

输出:

0xffffff00
0x00000021
0x789abc21
0x876543ff

改成对应的 python 代码:

x = 0x87654321
mask = 0xFF
print("0x%08x" % (~mask))
print("0x%08x" % (x & mask))
print("0x%08x" % (x ^ ~mask))
print("0x%08x" % (x | mask))

输出:

0x-0000100
0x00000021
0x-876543df
0x876543ff

请问 python 输出与 C 输出不一致的原因是什么,能否修改 python 代码使其与 C 输出一致?


Python中踩坑:Python的按位取反运算符~与C语言中的~有何区别?

7 回复

import ctypes

print(“0x%08x” % (ctypes.c_uint(~mask).value))


Python的按位取反运算符 ~ 在Python和C语言中的行为确实不同,核心区别在于Python的整数是任意精度的,而C语言的整数是固定宽度的。这导致 ~ 在两种语言中产生了看似不同的结果。

在C语言中,对于一个有符号的 int x(假设是32位),~x 的计算是直接的位翻转。例如:

int x = 5;        // 二进制: 0000 0000 0000 0000 0000 0000 0000 0101
int result = ~x;  // 二进制: 1111 1111 1111 1111 1111 1111 1111 1010
printf("%d", result); // 输出: -6

这里 -6 的补码表示正好是 ~5 的结果。

在Python中,整数没有固定的位数,~x 被定义为 -(x+1)。所以:

x = 5
print(~x)  # 输出: -6

从结果上看,~5 在两种语言中都输出 -6,似乎一样。但关键在于二进制表示和计算过程。

真正的“坑”出现在你试图用 ~ 处理一个无符号概念的整数,并期望得到特定的位模式时。 例如,在C语言中,如果你用 unsigned int

unsigned int x = 5;
unsigned int result = ~x;
printf("%u", result); // 输出一个很大的正数(4294967290,即2^32 - 6)

而在Python中,没有“无符号整数”类型。~5 永远等于 -6。如果你想要模拟C语言中无符号整数的按位取反行为,你需要明确地指定一个位宽并进行掩码操作:

x = 5
bit_width = 32
# 模拟32位无符号整数按位取反
unsigned_result = (~x) & ((1 << bit_width) - 1)
print(unsigned_result)  # 输出: 4294967290

或者,更直接地使用 0xFFFFFFFF 作为掩码:

print((~5) & 0xFFFFFFFF)  # 输出: 4294967290

总结一下关键点:

  1. 语义相同,实现不同:从数学结果看,~x 在两种语言中都等于 -(x+1)。但C语言是基于固定位宽的补码运算,Python是基于无限精度的整数运算。
  2. 无符号处理的区别:这是最容易踩坑的地方。C语言有明确的有符号/无符号类型,~ 的结果类型取决于操作数类型。Python没有无符号整数,~ 的结果永远是一个有符号整数(尽管是无限精度)。当你需要无语义的位操作时,必须手动进行位宽限制。
  3. 二进制表示:在C语言中,你可以直接观察到一个 int 变量在内存中所有位的翻转。在Python中,~5 的结果 -6 的二进制表示是 ...11111010(无限多个前导1),而不是一个固定32位或64位的模式。

所以,当你在Python中进行底层位操作、处理来自C语言库的数据或协议时,如果涉及 ~,一定要考虑位宽,并使用 & mask 来明确限定结果的位数,避免符号扩展带来的意外。

一句话建议:记住Python的~是数学运算,做位操作时要手动掩码限定宽度。

好像是被当成有符号数打印了。
试一下 &0xffffffff。这样
print(“0x%08x” % ((~mask)&0xffffffff))

<code>def foo(n, size=32):
return ((1<<size)-1)&n


x = 0x87654321
mask = 0xFF
print(“0x%08x” % foo(~mask))
print(“0x%08x” % foo(x & mask))
print(“0x%08x” % foo(x ^ ~mask))
print(“0x%08x” % foo(x | mask))
</code>

https://wiki.python.org/moin/BitwiseOperators

因为 Python 的 int 是无限长度的,如果对一个正整数取反,会变成负数,你可以认为符号位是无限长度的 1:

<br>&gt;&gt;&gt; mask = 0xFF<br>&gt;&gt;&gt; bin(mask)<br>'0b11111111'<br>&gt;&gt;&gt; ~mask<br>-256<br>&gt;&gt;&gt; bin(~mask)<br>'-0b100000000'<br>

bin(~mask)返回结果’-0b100000000’,前面的-0就是相当于无限长度的符号位,如果用 Two ‘ s Complement 来表示就全是 1.

解决这个问题也很简单,将结果按位与 0xFFFFFFFF (刚好 32 位 bit )运算,就可以消除掉超过 32 位的无限长度的符号位。

<br>&gt;&gt;&gt; ~mask &amp; 0xFFFFFFFF<br>4294967040<br>&gt;&gt;&gt; hex(_)<br>'0xffffff00'<br>

感谢楼上各位,4L 给了个很好的解释

这里不一致的原因其实就是 C 的 printf 的%x 对应的是 unsigned hexadecimal integer,而 python 的 print 的%x 对应的是 signed hexadecimal integer。

回到顶部