计算机内部以二进制形式存储数据,位运算(Bitwise Operations)直接对整数的二进制位进行操作,是C语言底层编程的重要特性。 位运算具有执行速度快、代码紧凑的优点,广泛应用于嵌入式系统、通信协议、图形处理、权限控制、数据加密等领域。 本章将详细讲解C语言的六种位运算符及其典型应用场景。
C语言提供了六种位运算符,它们只能用于整型操作数(char、short、int、long等,包括有符号和无符号)。
| 运算符 | 名称 | 说明 | 32 32& | 按位与 | 两个位都为1时结果为1,否则为0 | 32 32| | 按位或 | 两个位至少有一个为1时结果为1,否则为0 | 32 32^ | 按位异或 | 两个位不同时结果为1,相同时为0 | 32 32~ | 按位取反 | 单目运算符,将每个位取反(0变1,1变0) | 32 32<< | 左移 | 将二进制位向左移动指定的位数,右侧补0 | 32 32>> | 右移 | 将二进制位向右移动指定的位数,无符号数高位补0,有符号数高位补符号位(依赖实现) | 32
|---|
按位与常用于掩码操作,保留某些位,清零其他位。
#include <stdio.h>
int main() {
unsigned char a = 0b10101100; // 172
unsigned char b = 0b01101101; // 109
unsigned char c = a & b; // 00101100 (44)
printf("a & b = %u\n", c);
// 应用:提取低4位
unsigned char x = 0xAB; // 10101011
unsigned char low = x & 0x0F; // 00001011 (11)
printf("低4位: %u\n", low);
return 0;
}
按位或用于设置某些位为1,而不影响其他位。
#include <stdio.h>
int main() {
unsigned char a = 0b10101100; // 172
unsigned char b = 0b01101101; // 109
unsigned char c = a | b; // 11101101 (237)
printf("a | b = %u\n", c);
// 应用:设置第3位为1(从0开始计数)
unsigned char x = 0b10101010; // 170
unsigned char set = x | (1 << 3); // 10111010 (186)
printf("设置后: %u\n", set);
return 0;
}
按位异或用于翻转特定位,也可用于交换两个变量而不使用临时变量。
#include <stdio.h>
int main() {
unsigned char a = 0b10101100; // 172
unsigned char b = 0b01101101; // 109
unsigned char c = a ^ b; // 11000001 (193)
printf("a ^ b = %u\n", c);
// 应用:翻转第2位
unsigned char x = 0b10101010; // 170
unsigned char flip = x ^ (1 << 2); // 10101110 (174)
printf("翻转后: %u\n", flip);
// 交换两个整数(不使用临时变量)
int m = 10, n = 20;
m = m ^ n;
n = m ^ n;
m = m ^ n;
printf("交换后: m=%d, n=%d\n", m, n);
return 0;
}
按位取反将操作数的所有位取反,包括符号位。通常用于获得补码。
#include <stdio.h>
int main() {
unsigned char a = 0b10101100; // 172
unsigned char b = ~a; // 01010011 (83)
printf("~a = %u\n", b);
// 有符号整数的取反(注意:~a = -(a+1))
int x = 5;
int y = ~x; // -6
printf("~5 = %d\n", y);
return 0;
}
左移将二进制位向左移动指定的位数,右侧补0。左移n位相当于乘以2的n次方(不溢出时)。
#include <stdio.h>
int main() {
unsigned char a = 0b00001010; // 10
unsigned char b = a << 2; // 00101000 (40)
printf("10 << 2 = %u\n", b);
// 快速乘以2
int x = 7;
int y = x << 1; // 14
printf("7 * 2 = %d\n", y);
return 0;
}
右移将二进制位向右移动指定的位数。对于无符号数,高位补0(逻辑右移);对于有符号数,高位通常补符号位(算术右移),但具体行为依赖编译器实现。
#include <stdio.h>
int main() {
unsigned char a = 0b10101000; // 168
unsigned char b = a >> 2; // 00101010 (42)
printf("168 >> 2 = %u\n", b);
// 快速除以2(整数除法)
int x = 15;
int y = x >> 1; // 7(向下取整)
printf("15 / 2 = %d\n", y);
// 有符号数右移(算术右移)
int signedX = -8; // 二进制补码表示 ...11111000
int signedY = signedX >> 2; // 通常结果为 -2
printf("-8 >> 2 = %d\n", signedY);
return 0;
}
unsigned 类型。
使用位掩码可以紧凑地表示多个布尔标志。
#include <stdio.h>
// 权限标志
#define PERM_READ 0x01 // 0001
#define PERM_WRITE 0x02 // 0010
#define PERM_EXEC 0x04 // 0100
#define PERM_DELETE 0x08 // 1000
int main() {
unsigned char permissions = 0;
// 添加读和写权限
permissions |= PERM_READ | PERM_WRITE;
// 检查是否有读权限
if (permissions & PERM_READ) {
printf("具有读权限\n");
}
// 移除写权限
permissions &= ~PERM_WRITE;
// 检查写权限
if (!(permissions & PERM_WRITE)) {
printf("写权限已被移除\n");
}
// 切换执行权限(有则删,无则加)
permissions ^= PERM_EXEC;
return 0;
}
int isOdd(int n) {
return n & 1; // 最低位为1则为奇数
}
int powerOfTwo(int n) {
return 1 << n; // 2^n
}
int getBit(int num, int pos) {
return (num >> pos) & 1;
}
int setBit(int num, int pos) {
return num | (1 << pos);
}
int clearBit(int num, int pos) {
return num & ~(1 << pos);
}
void swap(int *a, int *b) {
*a = *a ^ *b;
*b = *a ^ *b;
*a = *a ^ *b;
}
int multiplyByPowerOfTwo(int x, int n) {
return x << n; // x * 2^n
}
int divideByPowerOfTwo(int x, int n) {
return x >> n; // x / 2^n(向下取整)
}
int absValue(int x) {
int mask = x >> (sizeof(int) * 8 - 1); // 符号位填充
return (x + mask) ^ mask;
}
#include <stdio.h>
// 将IP字符串转换为32位整数(网络字节序)
unsigned int ipToInt(const char *ip) {
unsigned int a, b, c, d;
sscanf(ip, "%u.%u.%u.%u", &a, &b, &c, &d);
return (a << 24) | (b << 16) | (c << 8) | d;
}
// 将32位整数转换为IP字符串
void intToIp(unsigned int ip, char *buffer) {
sprintf(buffer, "%u.%u.%u.%u",
(ip >> 24) & 0xFF,
(ip >> 16) & 0xFF,
(ip >> 8) & 0xFF,
ip & 0xFF);
}
int main() {
char ipStr[] = "192.168.1.100";
unsigned int ipInt = ipToInt(ipStr);
printf("%s 转换为整数: %u (0x%X)\n", ipStr, ipInt, ipInt);
char buffer[16];
intToIp(ipInt, buffer);
printf("整数 0x%X 转换回IP: %s\n", ipInt, buffer);
// 应用:获取网络号(假设子网掩码255.255.255.0)
unsigned int mask = 0xFFFFFF00;
unsigned int network = ipInt & mask;
printf("网络号: ");
intToIp(network, buffer);
printf("%s\n", buffer);
return 0;
}
&&、|| 是逻辑运算符,&、| 是位运算符,不要混淆。unsigned 类型。1 << 32 在32位系统上结果不确定。a & b == c 等价于 a & (b == c),应使用括号明确优先级。~0 在32位系统中是 0xFFFFFFFF,但若赋给有符号数,值为-1。要理解补码表示。int 的宽度可能不同(16位、32位、64位),位运算时应考虑可移植性。位运算是C语言高效编程的精髓之一,在系统编程、嵌入式开发、性能敏感场景中不可或缺。 掌握位运算,能让你编写出更紧凑、更底层的代码。后续的高级专题中,我们将进一步探讨位运算在数据结构与算法中的巧妙应用。