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;
}
posted @ 2025-08-28 19:30  一个行走者  阅读(40)  评论(0)    收藏  举报