C语言位运算符(bitwise)
1 判断一个数是否是2的幂
bool isPowerOfTwo(int n){
return n > 0 && ( n & ( n - 1 )) == 0;
}
基础回顾
&& - C语言中逻辑与运算符,符号左右两边同时满足则返回“True”。
& - C语言中的按位与(bitwise AND)。两个进制数在相同位置都为“1”时,返回“1”,否则返回“0”。
逻辑分析
n>0:排除非正数(负数和0)。2的幂必须是正整数(2的0次方等于1并非0)。负数不可能是2的幂(2的幂必须是正整数)。
( n & ( n - 1 )) == 0:理解这句代码我们需要知道2的幂数的二进制有什么特征。
2的幂的二进制特征
2⁰ = 1 → 0000 0001
2¹ = 2 → 0000 0010
2² = 4 → 0000 0100
2³ = 8 → 0000 1000
2⁴ = 16 → 0001 0000
2⁵ = 32 → 0010 0000
2⁶ = 64 → 0100 0000
2⁷ = 128 → 1000 0000
二进制中只有1个“1”,其余都是“0”
n & (n-1) 的特意之处
对与2的幂数与它小1的数关系
十进制 二进制
n = 8 1000
n-1 = 7 0111
n & (n-1) = 1000 & 0111 = 0000 = 0(表达式成立)
十进制 二进制
n = 16 0001 0000
n-1 = 15 0000 1111
n & (n-1) = 0001 0000 & 0000 1111 = 0000 0000 = 0 (表达式成立)
不是2的幂数与它小1的数关系
十进制 二进制
n = 6 0110
n-1 = 5 0101
n & (n-1) = 0110 & 0101 = 0100 = 4 ≠ 0(表达式不成立)
十进制 二进制
n = 7 0111
n-1 = 6 0110
n & (n-1) = 0111 & 0110 = 0110 = 6 ≠ 0 (表达式不成立)
2 判断一个整数是否是奇数
#include <stdbool.h>
bool isOddNumber(int num){
if (num == 0) return true;
return num & 1 == 1;
}
逻辑分析
0 是偶数,不是奇数。
num & 1 == 1: 检测二进制最低有效位是否是1,是1则是奇数。为什么会有这个判断观察下面的数字的二进制:
-2 是偶数二进制:11111111111111111111111111111110
-1 是奇数二进制:11111111111111111111111111111111
-4 是偶数二进制:11111111111111111111111111111100
-3 是奇数二进制:11111111111111111111111111111101
0 是偶数二进制:00000000000000000000000000000000
1 是奇数二进制:00000000000000000000000000000001
2 是偶数二进制:00000000000000000000000000000010
3 是奇数二进制:00000000000000000000000000000011
4 是偶数二进制:00000000000000000000000000000100
5 是奇数二进制:00000000000000000000000000000101
6 是偶数二进制:00000000000000000000000000000110
7 是奇数二进制:00000000000000000000000000000111
8 是偶数二进制:00000000000000000000000000001000
9 是奇数二进制:00000000000000000000000000001001
10 是偶数二进制:00000000000000000000000000001010
计算机存储都是按照补码存储。正整数的补码、源码、反码一样。 负数则不一样需要注意。负数源码=补码取反+1,然后将符号位置1。C语言中按位取反的运算符是“~”。
所有奇数的二进制右边第一位都是1,偶数都是0。因此就用表达式 num & 1 == 1。
取出4-7位
unsigned int hex=0x11223344
unsigned int mask=0xF0;
unsigned int 4to7bit= hex & mask;
printf(0x%x\n,4to7bit); // 0x40
第4-7位: 0x30
3 右移运算符(>>)
C语言中的右移运算(>>)的本质将一个整数的二进制向右移动指定位数。右移分为逻辑右移和算术右移。这两者的主要区别在于如何处理符号位。
-
什么是逻辑右移?
无符号整数向右移动指定的位数,左侧空出的位用“0”填充,右边溢出的位直接丢弃。 -
什么是算术右移?
有符号整数向右移动指定的位数,除符号位外左侧空出的位用“0”填充,右边溢出的位直接丢弃。 -
C的标准规定无符号整数必须使用逻辑右移。有符号整数则没有明确的规定,由编译器实现决定。大多数编译器如GCC,Clang对有符号数使用算术右移以保持符号。
| 整数 | 二进制(补码) | 右移3位二进制(补码) | 右移4位二进制(补码) |
|---|---|---|---|
| 5678 | 00000000 00000000 00010110 00101110 | 00000000 00000000 00000010 11000101 | 00000000 00000000 00000001 01100010 |
| -5678 | 11111111 11111111 11101001 11010010 | 11111111 11111111 11111101 00111010 | 11111111 11111111 11111110 10011101 |
include <stdio.h>
void print_binary(int num, int bits){
unsigned int n = *(unsigned int *)& num;
for (int i = sizeof(n) * bits - 1; i >= 0; i++){
printf("%d", (n>> i) & 1);
if (i % bits == 0 && i != 0){
printf(" ");
}
}
}
int main(void){
int n = 5678;
print_binary(n, 8); // 00000000 00000000 00010110 00101110
print_binary(n>>3, 8); //00000000 00000000 00000010 11000101
print_binary(n>>5, 8); //00000000 00000000 00000000 10110001
int n1 = -5678;
print_binary(n1, 8); // 11111111 11111111 11101001 11010010
print_binary(n1>>3, 8); // 11111111 11111111 11111101 00111010
print_binary(n1>>5, 8); // 11111111 11111111 11111111 01001110
int N2 = 97;
printf("右移1位:%d\n", N2>>1); // 右移1位:48
printf("右移2位:%d\n", N2>>2); // 右移2位:24
printf("右移3位:%d\n", N2>>3); // 右移3位:12
printf("右移4位:%d\n", N2>>4); // 右移4位:6
int N3 = -97;
printf("右移1位:%d\n", N3>>1); // 右移1位:-49
printf("右移2位:%d\n", N3>>2); // 右移2位:-25
printf("右移3位:%d\n", N3>>3); // 右移3位:-13
printf("右移4位:%d\n", N3>>4); // 右移4位:-7
unsigned int N4 = 89;
printf("右移1位:%d\n", N4>>1); // 右移1位:44
printf("右移2位:%d\n", N4>>2); // 右移2位:22
printf("右移3位:%d\n", N4>>3); // 右移3位:11
printf("右移4位:%d\n", N4>>4); // 右移4位:5
// 有符号正整数右移:每右移1位,比原数大概少了一半。正整数会向下取整(类似四舍五入的舍)
// 有符号负整数右移:每右移1位,比原数大概少了一半。有符号负整数会向上取整(类似四舍五入的入)
// 无符号整数右移:每右移1位,比原数大概少了一半。无符号整数会向下取整(类似四舍五入的舍)
return;
}

浙公网安备 33010602011771号