Skip to content

C# - 位运算符

C# 中的位运算符在二进制级别上对整型数据执行操作。这些运算符对于底层编程任务至关重要,例如性能优化、与硬件交互、实现加密算法以及操作标志枚举(Flag Enums)。

位运算符操作其操作数的单个位。要理解它们,关键是要以二进制(Base-2)表示来思考数字。

下表列出了 C# 支持的位运算符。假设变量 a 的值为 60(二进制 0011 1100),变量 b 的值为 13(二进制 0000 1101):

运算符描述示例
&二进制与(AND): 如果两个操作数中都存在某个位,则将该位复制到结果中。(a & b) 的结果是 12(二进制 0000 1100)
|二进制或(OR): 如果任一操作数中存在某个位,则将该位复制到结果中。(a | b) 的结果是 61(二进制 0011 1101)
^二进制异或(XOR,Exclusive OR): 如果某位只在一个操作数中设置(存在)而不存在于两个操作数中,则将该位复制到结果中。(a ^ b) 的结果是 49(二进制 0011 0001)
~二进制按位取反(Ones’ Complement): 一元运算符,用于翻转其操作数的位。(~a) 的结果是 -61。这是因为整型使用补码(Two’s Complement)表示负数。
<<左移(Left Shift): 将左操作数的位向左移动右操作数指定的位数。右侧新填充的位为零。a << 2 的结果是 240(二进制 1111 0000)
>>右移(Right Shift): 将左操作数的位向右移动。如果操作数是有符号类型,则使用符号位填充左侧新位(算术右移);如果是无符号类型,则使用零填充。a >> 2 的结果是 15(二进制 0000 1111)

让我们通过现代 C# 代码示例来探索每个运算符。这些示例使用 C# 9 及更高版本中提供的顶级语句(top-level statements),以提高简洁性。

示例:按位与 (&)、或 (|) 和异或 (^)

Section titled “示例:按位与 (&)、或 (|) 和异或 (^)”
int a = 5; // 二进制:0101
int b = 3; // 二进制:0011
// 按位与:0101 & 0011 = 0001 (十进制 1)
int andResult = a & b;
Console.WriteLine($"Bitwise AND: {andResult}"); // 输出:Bitwise AND: 1
// 按位或:0101 | 0011 = 0111 (十进制 7)
int orResult = a | b;
Console.WriteLine($"Bitwise OR: {orResult}"); // 输出:Bitwise OR: 7
// 按位异或:0101 ^ 0011 = 0110 (十进制 6)
int xorResult = a ^ b;
Console.WriteLine($"Bitwise XOR: {xorResult}"); // 输出:Bitwise XOR: 6

按位取反运算符可能有点复杂。它会反转所有位。对于一个 int(32 位有符号整数),值 5 的二进制表示是 0000...00000101。反转它将得到 1111...11111010。在 C# 的补码(Two’s Complement)系统中,这个二进制模式表示 -6。

int a = 5; // 二进制(32位):00000000 00000000 00000000 00000101
int result = ~a;
Console.WriteLine($"Bitwise NOT: {result}"); // 输出:Bitwise NOT: -6

移位运算符是乘以或除以 2 的幂的有效方式。

// 左移(等同于乘以 2 的 n 次方)
int num = 5; // 二进制:00000101
int leftShifted = num << 2; // 向左移 2 位:00010100 (十进制 20)
Console.WriteLine($"Left Shift (5 << 2): {leftShifted}"); // 输出:Left Shift (5 << 2): 20
// 右移(等同于除以 2 的 n 次方)
int anotherNum = 20; // 二进制:00010100
int rightShifted = anotherNum >> 2; // 向右移 2 位:00000101 (十进制 5)
Console.WriteLine($"Right Shift (20 >> 2): {rightShifted}"); // 输出:Right Shift (20 >> 2): 5

现代实际应用:标志枚举(Flag Enums)

Section titled “现代实际应用:标志枚举(Flag Enums)”

在现代 C# 中,位运算符的一个常见且强大的用途是使用带有 [Flags] 特性(attribute)修饰的枚举(enum)来管理一组选项或权限。

[Flags]
enum Permissions
{
None = 0, // 二进制:0000
Read = 1 << 0, // 二进制:0001 (1)
Write = 1 << 1, // 二进制:0010 (2)
Execute = 1 << 2, // 二进制:0100 (4)
All = Read | Write | Execute // 二进制:0111 (7)
}
// 使用或运算符(OR)分配多个权限
Permissions userPermissions = Permissions.Read | Permissions.Write;
Console.WriteLine($"User permissions: {userPermissions}");
// 使用与运算符(AND)检查权限
if ((userPermissions & Permissions.Read) == Permissions.Read)
{
Console.WriteLine("User has Read permission.");
}
// 使用或运算符(OR)添加权限
userPermissions |= Permissions.Execute;
Console.WriteLine($"After adding Execute: {userPermissions}");
// 使用与运算符(AND)结合非运算符(NOT)移除权限
userPermissions &= ~Permissions.Write;
Console.WriteLine($"After removing Write: {userPermissions}");
场景陷阱解决方案 / 最佳实践
运算符优先级位运算符的优先级低于比较运算符。x & y == 0 会被计算为 x & (y == 0)。始终使用括号来确保正确的运算顺序,例如 (x & y) == 0。
有符号数与无符号数由于补码和算术移位,~ 和 >> 运算符在处理有符号数时可能产生意想不到的结果。根据您的需求,明确使用有符号类型(int、long)或无符号类型(uint、ulong)。如果您纯粹操作位模式,请使用无符号类型。
魔法数字(Magic Numbers)使用原始整数字面量作为掩码(例如 user.Permissions & 4)会使代码难以阅读和维护。使用 [Flags] 枚举或命名常量来定义位掩码,以提高代码的清晰度和类型安全。